使用 Azure Key Vault 签署 JWT 令牌

我正在使用私钥来签署 JWT 令牌,它按预期工作。但是,我想利用 Azure Key Vault 为我进行签名,以便私钥不会离开 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.KeysAzure.Identitynuget 包)

This doesn't work. The first two parts of the token - ie. header and payload are identical to the JWT that does work. The only thing that's different is the signature at the end.

I'm out of ideas! Note that this is closely related to this Stackoverflow question, where the answers seem to suggest what I'm doing should be correct.

在此处输入图片说明

Heath

Your code is mostly correct, though you should use either Encoding.UTF8 or Encoding.ASCII (since the base64url characters are all valid ASCII and you eliminate any BOM concerns) to get the bytes for headerAndPayload.

我能够让它工作,并发现https://jwt.io当它说你可以粘贴公钥或证书时相当模糊。它必须是 PEM 编码的,如果发布 RSA 公钥,您必须使用不太常见的“BEGIN RSA PUBLIC KEY”标签,而不是更常见的“BEGIN PUBLIC KEY”标签。

我尝试了一些应该都有效的方法,当我发现使用 Key Vault 的证书对“BEGIN CERTIFICATE”产生影响时,我又尝试使用“BEGIN PUBLIC KEY”。直到我一时兴起将其更改为“BEGIN RSA PUBLIC KEY”时,JWT 才被成功验证。

下面是我尝试使用证书 URI 的代码:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;
using Microsoft.IdentityModel.Tokens;

var arg = args.Length > 0 ? args[0] : throw new Exception("Key Vault key URI required");
var uri = new Uri(arg, UriKind.Absolute);

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Iss, Guid.NewGuid().ToString()),
    new Claim(JwtRegisteredClaimNames.Aud, "https://test.example.com"),
    new Claim(JwtRegisteredClaimNames.Exp, DateTimeOffset.Now.AddMinutes(10).ToUnixTimeSeconds().ToString()),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};

var header = @"{""alg"":""RS256"",""typ"":""JWT""}";
var payload = JsonSerializer.Serialize(new JwtPayload(claims));
var headerAndPayload = $"{Base64UrlEncoder.Encode(header)}.{Base64UrlEncoder.Encode(payload)}";

var id = new KeyVaultKeyIdentifier(uri);
var credential = new DefaultAzureCredential();

var certClient = new CertificateClient(id.VaultUri, credential);
KeyVaultCertificate cert = await certClient.GetCertificateAsync(id.Name);
using X509Certificate2 pfx = await certClient.DownloadCertificateAsync(id.Name, id.Version);

var pem = PemEncoding.Write("CERTIFICATE".AsSpan(), pfx.RawData);
Console.WriteLine($"Certificate (PEM):\n");
Console.WriteLine(pem);
Console.WriteLine();

using var rsaKey = pfx.GetRSAPublicKey();
var pubkey = rsaKey.ExportRSAPublicKey();
pem = PemEncoding.Write("RSA PUBLIC KEY".AsSpan(), pubkey.AsSpan());
Console.WriteLine($"Public key (PEM):\n");
Console.WriteLine(pem);
Console.WriteLine();

var cryptoClient = new CryptographyClient(cert.KeyId, credential);

using var sha256 = SHA256.Create();
var digest = sha256.ComputeHash(Encoding.ASCII.GetBytes(headerAndPayload));
var signature = (await cryptoClient.SignAsync(SignatureAlgorithm.RS256, digest)).Signature;

var token = $"{headerAndPayload}.{Base64UrlEncoder.Encode(signature)}";
Console.WriteLine($"JWT:\n\n{token}");

对于仅使用密钥,以下应该有效:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Azure.Identity;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;
using Microsoft.IdentityModel.Tokens;

var arg = args.Length > 0 ? args[0] : throw new Exception("Key Vault key URI required");
var uri = new Uri(arg, UriKind.Absolute);

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Iss, Guid.NewGuid().ToString()),
    new Claim(JwtRegisteredClaimNames.Aud, "https://test.example.com"),
    new Claim(JwtRegisteredClaimNames.Exp, DateTimeOffset.Now.AddMinutes(10).ToUnixTimeSeconds().ToString()),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};

var header = @"{""alg"":""RS256"",""typ"":""JWT""}";
var payload = JsonSerializer.Serialize(new JwtPayload(claims));
var headerAndPayload = $"{Base64UrlEncoder.Encode(header)}.{Base64UrlEncoder.Encode(payload)}";

var id = new KeyVaultKeyIdentifier(uri);
var credential = new DefaultAzureCredential();

var keyClient = new KeyClient(id.VaultUri, credential);
KeyVaultKey key = await keyClient.GetKeyAsync(id.Name, id.Version);

using var rsaKey = key.Key.ToRSA();
var pubkey = rsaKey.ExportRSAPublicKey();
var pem = PemEncoding.Write("RSA PUBLIC KEY".AsSpan(), pubkey.AsSpan());
Console.WriteLine($"Public key (PEM):\n");
Console.WriteLine(pem);
Console.WriteLine();

var cryptoClient = new CryptographyClient(key.Id, credential);

using var sha256 = SHA256.Create();
var digest = sha256.ComputeHash(Encoding.ASCII.GetBytes(headerAndPayload));
var signature = (await cryptoClient.SignAsync(SignatureAlgorithm.RS256, digest)).Signature;

var token = $"{headerAndPayload}.{Base64UrlEncoder.Encode(signature)}";
Console.WriteLine($"JWT:\n\n{token}");

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用 Azure Key Vault + Azure Function Apps + CA 证书签署 PDF 文档时出错

使用 azure 的 SignAsync 方法签署自定义创建的 JWT 格式令牌并离线验证

用于API和OAUTH策略的Azure Vault密钥的JWT令牌

使用@ azure / identity访问Key Vault并得到错误“请求缺少承载或PoP令牌错误401”

是否可以使用 Azure Key Vault 的 Keys API 创建 JSON Web 令牌?

使用 AzukeKeyVault 密钥签署 JWT

为什么使用Azure Key Vault

使用Java验证Azure Key Vault

使用公司代理实施Azure Key Vault

使用 DataProtectionProvider 连接到 Azure Key Vault

尝试使用 Azure Functions 在 Azure Key Vault 中创建机密

使用serviceAccountId时无法签署JWT

使用Azure Key Vault存储用户名和密码

如何使用Moq Azure Key Vault进行单元测试

使用 arm 模板部署 azure Key Vault Secret 出现错误

如何使用 DefaultAzureCredential 从 Azure Key Vault 检索 Secret

使用API在Azure Key Vault中创建密钥

使用Azure Key Vault进行Terraform获取秘密价值

使用前缀从Azure Key Vault获取所有秘密

如何在Android中使用Azure Key Vault

PowerShell 使用 Az.KeyVault 从 Azure Key Vault 导出 Pfx

如何使用 Terraform 11.14 启用 Azure Key Vault 日志记录?

如何在链接模板中使用 Azure Key Vault 机密

使用MSI访问C#.Net中的Azure Key Vault

使用客户端JavaScript从Azure访问Key Vault?

如何使用Azure Key Vault加密SQL中的数据列?

在 AKS 中使用管理标识的 Azure Key Vault

Google Cloud Key Management Service签署JSON Web令牌

Azure Key Vault Chef 食谱