我正在使用私钥对JWT令牌进行签名,这按预期工作。但是,我希望利用Azure密钥库为我进行签名,以便私钥不会离开KeyVault。我很难让它工作,但不确定原因。
以下代码 * 不 * 使用KeyVault,但 * 确实 * 工作...
var handler = new JwtSecurityTokenHandler();
var expiryTime = DateTimeOffset.UtcNow.AddMinutes(10).ToUnixTimeSeconds();
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Iss, clientId),
new Claim(JwtRegisteredClaimNames.Sub, integrationUser),
new Claim(JwtRegisteredClaimNames.Aud, "https://test.example.com"),
new Claim(JwtRegisteredClaimNames.Exp, expiryTime.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // Add JTI for additional security against replay attacks
};
var privateKey = File.ReadAllText(@"selfsigned.key")
.Replace("-----BEGIN PRIVATE KEY-----", "")
.Replace("-----END PRIVATE KEY-----", "");
var privateKeyRaw = Convert.FromBase64String(privateKey);
var provider = new RSACryptoServiceProvider();
provider.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(privateKeyRaw), out _);
var rsaSecurityKey = new RsaSecurityKey(provider);
var token = new JwtSecurityToken
(
new JwtHeader(new SigningCredentials(rsaSecurityKey, SecurityAlgorithms.RsaSha256)),
new JwtPayload(claims)
);
var token = handler.WriteToken(token);
这是可行的,如果我将JWT复制到jwt.io中,并粘贴公钥-它表示签名已验证...
这个令牌也可以对我调用的API起作用。
但是,如果使用KeyVault签名...
var handler = new JwtSecurityTokenHandler();
var expiryTime = DateTimeOffset.UtcNow.AddMinutes(10).ToUnixTimeSeconds();
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Iss, clientId),
new Claim(JwtRegisteredClaimNames.Sub, integrationUser),
new Claim(JwtRegisteredClaimNames.Aud, "https://test.example.com"),
new Claim(JwtRegisteredClaimNames.Exp, expiryTime.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // Add JTI for additional security against replay attacks
};
var header = @"{""alg"":""RS256"",""typ"":""JWT""}";
var payload = JsonConvert.SerializeObject(new JwtPayload(claims));
var headerAndPayload = $"{Base64UrlEncoder.Encode(header)}.{Base64UrlEncoder.Encode(payload)}";
// Sign token
var credential = new InteractiveBrowserCredential();
var client = new KeyClient(vaultUri: new Uri(kvUri), credential);
var key = (KeyVaultKey)client.GetKey("dan-test");
var cryptoClient = new CryptographyClient(keyId: key.Id, credential);
var digest = new SHA256CryptoServiceProvider().ComputeHash(Encoding.Unicode.GetBytes(headerAndPayload));
var signature = await cryptoClient.SignAsync(SignatureAlgorithm.RS256, digest);
var token = $"{headerAndPayload}.{Base64UrlEncoder.Encode(signature.Signature)}";
- (使用
Azure.Security.KeyVault.Keys
和Azure.Identity
块体封装)*
这不起作用。令牌的前两部分--即头和有效负载与起作用的JWT完全相同。唯一不同的是末尾的签名。
我没有主意了!请注意,这与this Stackoverflow question密切相关,其中的答案似乎表明我所做的应该是正确的。
2条答案
按热度按时间mccptt671#
您的代码基本上是正确的,但是您应该使用
Encoding.UTF8
或Encoding.ASCII
(因为base64url字符都是有效的ASCII,并且您消除了任何BOM问题)来获取headerAndPayload
的字节。我能够让这个工作,并发现https://jwt.io是相当模糊的,当它说你可以粘贴公钥或证书。它必须是PEM编码,如果张贴RSA公钥,你必须使用不太常见的“开始RSA PUBLIC KEY”标签,而不是更常见的“BEGIN PUBLIC KEY”。
我尝试了几个应该都能成功的方法,当我发现使用Key Vault的证书与“开始CERTIFICATE”没有关系时,我又尝试了“BEGIN PUBLIC KEY”。直到我一时兴起,将其改为“BEGIN RSA PUBLIC KEY”,JWT才成功通过验证。
下面是我尝试使用证书URI的代码:
如果只使用一个密钥,则应执行以下操作:
46scxncf2#
要生成令牌,您可以在
SigningCredentials
中创建自己的CryptoProviderFactory
实现。SignatureProviderFactory
实施:CustomSignatureProvider
实施