使用 System.IdentityModel.Tokens.Jwt 使用 x5c header 参数创建 JWT 令牌

Creating JWT token with x5c header parameter using System.IdentityModel.Tokens.Jwt

我的项目正在构建基于 .NET Core 和 System.IdentityModel.Tokens.Jwt nuget 包的身份验证服务。我们想要创建包含可用于验证 JWT 数字签名的 public 密钥证书(或证书链)的 JWT 令牌。这可以通过商业身份提供者 (SaaS) 实现,并通过名为“x5c”的 header 参数在 JWT 规范中得到支持。但到目前为止,我一直无法使用 System.IdentityModel.Tokens.Jwt.

使其正常工作

我能够创建使用证书签名的 JWT 令牌。证书是 self-signed 并使用 openssl 创建(命令包含在下方)。 我在 C# 中的测试代码如下所示:

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
// more usings..

public static string GenerateJwtToken(int exampleAccountId, string x509CertFilePath, string x509CertFilePassword)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var signingCert = new X509Certificate2(x509CertFilePath, x509CertFilePassword);

    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, exampleAccountId.ToString()) }),
        Expires = DateTime.UtcNow.AddDays(30),
        Audience = "myapp:1",
        Issuer = "self",
        SigningCredentials = new X509SigningCredentials(signingCert, SecurityAlgorithms.RsaSha512Signature),
        Claims = new Dictionary<string, object>()
        {
            ["test1"] = "hello world",
            ["test2"] = new List<int> { 1, 2, 4, 9 }
        }
    };

    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}

生成的令牌 header 在 jwt.io 中反序列化为:

{
  "alg": "RS512",
  "kid": "193A49ED67F22850F4A95258FF07571A985BFCBE",
  "x5t": "GTpJ7WfyKFD0qVJY_wdXGphb_L4",
  "typ": "JWT"
}

事实是,我也想获得“x5c”header 参数输出。这样做的原因是我的项目试图包含带有 public 密钥的证书以验证令牌本身内部的令牌签名,而“x5c”是执行此操作的好方法。但我就是无法让它工作。

我尝试在 SecurityTokenDescriptor 上使用 AdditionalHeaderClaims 手动添加 x5c,但它并没有在令牌中输出。

有人知道怎么做吗,或者你能给我指出一些关于这个主题的可靠资源吗?

顺便说一下,这是我生成使用的证书的方式(在 Windows 上):

openssl genrsa -out private2048b.key 2048 

openssl req -new -key private2048b.key -out myrequest2048.csr -config <path to openssl.cfg>

openssl x509 -req -days 3650 -in myrequest2048.csr -signkey private2048b.key -out public2048b.crt

openssl pkcs12 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in public2048b.crt -inkey private2048b.key -out mypkcs2048.pfx -name "Testtest"

PFX 是代码中正在读取和使用的文件。

后代更新

使用 Abdulrahman Falyoun 的回答,代码的最后部分已更新为使用 token.Header.Add 手动添加“x5c” header 参数,然后序列化 JWT 令牌。必须将令牌转换为 JwtSecurityToken。 这行得通,并在 https://jwt.io 中创建了一个有效的令牌(并且具有可以立即验证的签名):

// create JwtSecurityTokenHandler and SecurityTokenDescriptor instance before here..

var exportedCertificate = Convert.ToBase64String(signingCert.Export(X509ContentType.Cert, x509CertFilePassword));
 
// Add x5c header parameter containing the signing certificate:
var token = tokenHandler.CreateToken(tokenDescriptor) as JwtSecurityToken;
token.Header.Add(JwtHeaderParameterNames.X5c, new List<string> { exportedCertificate });
 
return tokenHandler.WriteToken(token);

x5c是什么?

The "x5c" (X.509 certificate chain) Header Parameter contains the X.509 public key certificate or certificate chain [RFC5280] corresponding to the key used to digitally sign the JWS. The certificate or certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (not base64url-encoded) DER [ITU.X690.2008] PKIX certificate value. The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the one used to certify the previous one. The recipient MUST validate the certificate chain according to RFC 5280 [RFC5280] and consider the certificate or certificate chain to be invalid if any validation failure occurs. The use of this Header Parameter is OPTIONAL.

备注

From the security point of view - do not use the x5c certificate to validate the signature directly. In that case, anybody could just provide their own certificate and spoof any identity. The purpose of the x5t / x5t#S256 header is to identify the signer - check you trust the certificate provided by x5c or x5t#S256 (or its issuer) under the specified iss, only then you should validate the signature.

所以构建X509链

X509Chain chain = new X509Chain()
bool success = chain.Build(cert);

if (!success) throw Error

然后对于每个 chain.ChainElements 值,获取证书 属性 RawValue 属性(并对其进行 base64 编码)。

最后,您得到了 x5c 的字符串,应该只将它提供给 jwt 的 header。

查看以下链接

希望有用。

#编辑

如果问题是将 x5c 提供给 header,您必须使用

添加它
token.Header.Add(name, value)