[英]Azure KeyVault - Sign JWT Token
我开始使用 Azure Keyvault 为我的应用程序存储私钥。
我有一个用例,我需要使用 RSA 私钥对 JWT 令牌进行签名。
当我的应用程序内存中有私钥时,这很容易,我会这样做
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...,
signingCredentials_PrivateKey);
现在我开始使用 Azure Keyvault,我想看看是否可以通过KeyVaultClient.SignAsync
方法对 JWT 令牌进行KeyVaultClient.SignAsync
。
类似的东西
KeyVaultClient client = ...;
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...);
var tokenString = client.SignAsync(myKeyIdentifier, token);
首先,JWT 令牌由三部分组成:Header、Payload 和 Signature。 它们都是 Base64UrlEncoded。
您可以通过以下方式获得签名:
HMAC-SHA256(
base64urlEncoding(header) + '.' + base64urlEncoding(payload),
secret
)
因此,您需要生成标头和有效负载,将它们按点组合,计算散列,然后才能获得签名。
这是一个示例供您参考:
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 的Wiki
我最终使用了杰克贾的答案
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()
万一有人需要离线验证。 我使用 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 };
然后当客户端需要验证令牌时
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);
请注意,如果您让 Azure Key Vault 为您的令牌签名,则需要注意一个速率限制因素。 我认为最好从 AKV 下载私钥,然后在本地签署我的令牌。
在此链接阅读更多信息
对于任何寻求宽松方法的人(也就是说,不使用 KeyVaultClient,而是使用新的 Azure API):
https://docs.microsoft.com/en-us/dotnet/api/azure.security.keyvault.keys.cryptography.cryptographyclient?view=azure-dotnet将允许您调用接受 SignatureAlgorithm 和您的数据(以字节 [] 或 Stream 形式)和取消令牌。 它返回一个 SignatureResult ,可用于检索作为 byte[] 和各种其他信息的实际签名。
SignDataAsync 在这里将在“.”连接的 base64url 编码标头和有效负载上调用(如 rfc7515 中所述)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.