繁体   English   中英

Azure KeyVault - 签署 JWT 令牌

[英]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带有对SecurityTokenICryptoProvider扩展支持 KeyVault。

我的问题是

  1. 我无法使用此解决方案重复使用KeyVaultClient的现有实例。
  2. 它正在阻塞(在幕后,它在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM