Azure KeyVault - 签署 JWT 令牌
Posted
技术标签:
【中文标题】Azure KeyVault - 签署 JWT 令牌【英文标题】:Azure KeyVault - Sign JWT Token 【发布时间】:2019-11-17 15:09:13 【问题描述】:我开始使用 Azure Keyvault 来存储我的应用程序的私钥。
我有一个用例,我需要使用 RSA 私钥签署 JWT 令牌。
当我的应用程序内存中有私钥时,这很容易, 我会这样做
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...,
signingCredentials_PrivateKey);
现在我开始使用 Azure Keyvault,我想看看是否可以通过 KeyVaultClient.SignAsync
方法签署 JWT 令牌。
类似
KeyVaultClient client = ...;
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...);
var tokenString = client.SignAsync(myKeyIdentifier, token);
【问题讨论】:
【参考方案1】:首先,一个 JWT 令牌由三部分组成:Header、Payload 和 Signature。都是Base64UrlEncoded。
你可以得到如下签名:
HMAC-SHA256(
base64urlEncoding(header) + '.' + base64urlEncoding(payload),
secret
)
所以,你需要生成header和payload,将它们按点组合,计算hash,然后你就可以得到签名了。
这是一个示例供您参考:
var byteData = Encoding.Unicode.GetBytes(base64urlEncoding(header) + "." + base64urlEncoding(payload));
var hasher = new SHA256CryptoServiceProvider();
var digest = hasher.ComputeHash(byteData);
var signature = await keyClient.SignAsync(keyIdentifier, "RS256", digest);
var token = base64urlEncoding(header) + "." + base64urlEncoding(payload) + "." + base64urlEncoding(signature)
SignAsync 的官方 SDK 文档
JWT 的维基
【讨论】:
我找到了另一种解决方案,但最终使用了您的解决方案。看我的回答。 @user10962730 我在我身边测试,并获得了成功。请参阅我的新答案。【参考方案2】:我最终使用了 Jack Jia 的答案
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate);
var header = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(new Dictionary<string, string>()
JwtHeaderParameterNames.Alg, "RS256" ,
JwtHeaderParameterNames.Kid, "https://myvault.vault.azure.net/keys/mykey/keyid" ,
JwtHeaderParameterNames.Typ, "JWT"
));
var byteData = Encoding.UTF8.GetBytes(header + "." + token.EncodedPayload);
var hasher = new SHA256CryptoServiceProvider();
var digest = hasher.ComputeHash(byteData);
var signature = await _keyVault.SignAsync("https://myvault.vault.azure.net/keys/mykey/keyid", "RS256", digest);
return $"header.token.EncodedPayload.Base64UrlEncoder.Encode(signature.Result)";
我找到了另一种解决方案,我不太喜欢它,但它与 JWT 库“集成”得更好。
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate,
new SigningCredentials(new KeyVaultSecurityKey("https://myvault.vault.azure.net/keys/mykey/keyid", new KeyVaultSecurityKey.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)), "RS256")
CryptoProviderFactory = new CryptoProviderFactory() CustomCryptoProvider = new KeyVaultCryptoProvider()
);
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(token);
原来有一个库 Microsoft.IdentityModel.KeyVaultExtensions
扩展为 SecurityToken
和 ICryptoProvider
支持 KeyVault。
我的问题是
-
我无法通过此解决方案重用
KeyVaultClient
的现有实例。
它正在阻塞(在幕后,它在KeyVaultClient.SignAsync
上调用.GetAwaiter().GetResult()
【讨论】:
我测试了您的解决方案。这是完美的。 @JackJia 我很好奇你是否研究过 Tore Nestenius 在他的回答中提到的限速因素?【参考方案3】:如果有人需要离线验证。我使用 user10962730 的答案来生成令牌,并使用以下内容进行离线验证:
在线时,我们的客户端将从我们的 API 中检索公共信息。 RSA 模数和指数将被传输 Base64Url 编码而不是字节数组
var keyBundle = await _keyVault.GetKeyAsync("https://myvault.vault.azure.net/keys/mykey/keyid");
return new n = keyBundle.N, e = keyBundle.E ;
那么当客户端需要验证一个token时
string jwtToken = "[Header].[Payload].[Signature]";
var jwtParts = jwtToken.Split(".");
var rsa = new RSACryptoServiceProvider();
var p = new RSAParameters() Modulus = Base64UrlEncoder.DecodeBytes(key.n), Exponent = Base64UrlEncoder.DecodeBytes(key.e) ;
rsa.ImportParameters(p);
var dataToHash = Encoding.UTF8.GetBytes($"jwtParts[0].jwtParts[1]");
byte[] digestBytes = SHA256.Create().ComputeHash(dataToHash);
var sigBytes = Base64UrlEncoder.DecodeBytes(jwtParts[2]);
var isVerified = rsa.VerifyHash(digestBytes, "Sha256", sigBytes);
【讨论】:
【参考方案4】:请注意,如果您让 Azure Key Vault 签署您的令牌,那么您需要注意一个速率限制因素。我认为最好从 AKV 下载私钥,然后在本地签署我的令牌。
在link了解更多信息
【讨论】:
这很好,但“更好”取决于几个因素。如果目标是保密私钥,那么更好的方法可能是限制您自己的客户端以响应来自 Key Vault 的 429 响应。如果最近有任何来自 Key Vault 的 429 响应,您还可以尝试使用断路器或高水位线模式来实现一些背压,您可以在向 Key Vault 发出实际请求之前发送自己的 429 响应。跨度> 【参考方案5】:对于寻求不那么宽松的方法(也就是说,不使用 KeyVaultClient 并使用新的 Azure API)的任何人:
https://docs.microsoft.com/en-us/dotnet/api/azure.security.keyvault.keys.cryptography.cryptographyclient?view=azure-dotnet 将允许您调用“SignDataAsync”方法,该方法接受 SignatureAlgorithm 和您的数据(以字节 [] 或流形式)和取消令牌。它返回一个 SignatureResult,可用于检索实际签名为 byte[] 和各种其他信息。
此处将在“.”级联的 base64url 编码的标头和有效负载上调用 SignDataAsync(如 rfc7515 中所述)。
【讨论】:
以上是关于Azure KeyVault - 签署 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章
使用 Azure.Security.KeyVault.Secrets 的网络核心密钥保管库配置
Azure Keyvault 通过 ARM 添加 Function MSI
使用 KeyVault 和 Azure PowerShell 证书身份验证创建 Azure AD 应用程序
Azure 函数和 Azure KeyVault 通过服务端点进行通信
Azure KeyVault:Azure.Identity.CredentialUnavailableException:DefaultAzureCredential 无法从包含的凭据中检索令牌