使用 System.IdentityModel.Tokens.Jwt 创建带有 x5c 标头参数的 JWT 令牌

Posted

技术标签:

【中文标题】使用 System.IdentityModel.Tokens.Jwt 创建带有 x5c 标头参数的 JWT 令牌【英文标题】:Creating JWT token with x5c header parameter using System.IdentityModel.Tokens.Jwt 【发布时间】:2021-01-09 18:12:09 【问题描述】:

我的项目正在构建一个基于 .NET Core 和 System.IdentityModel.Tokens.Jwt nuget 包的身份验证服务。我们希望创建包含可用于验证 JWT 数字签名的公钥证书(或证书链)的 JWT 令牌。这对于商业身份提供商 (SaaS) 来说是可能的,并且在 JWT 规范中通过称为“x5c”的标头参数得到支持。但到目前为止,我无法使用System.IdentityModel.Tokens.Jwt 来实现这一点。

我能够创建使用证书签名的 JWT 令牌。证书是自签名的,并使用 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);

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


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

问题是,我也想获得“x5c”标头参数输出。这样做的原因是我的项目试图将证书与公钥一起包含在令牌本身中以验证令牌签名,而“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 在序列化 JWT 令牌之前手动添加“x5c”标头参数。令牌必须转换为 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);

【问题讨论】:

【参考方案1】:

什么是 x5c?

“x5c”(X.509 证书链)头参数包含与用于对 JWS 进行数字签名的密钥对应的 X.509 公钥证书或证书链 [RFC5280]。证书或证书链表示为证书值字符串的 JSON 数组。数组中的每个字符串都是 base64 编码(不是 base64url 编码)DER [ITU.X690.2008] PKIX 证书值。包含与用于对 JWS 进行数字签名的密钥对应的公钥的证书必须是第一个证书。这之后可以是附加证书,每个后续证书都是用于证明前一个证书的证书。接收者必须根据 RFC 5280 [RFC5280] 验证证书链,如果发生任何验证失败,则认为证书或证书链无效。此标头参数的使用是可选的。

注意

从安全的角度来看 - 不要使用 x5c 证书直接验证签名。在这种情况下,任何人都可以提供自己的证书并欺骗任何身份。 x5t / x5t#S256 标头的目的是识别签名者 - 检查您信任指定 iss 下的 x5c 或 x5t#S256(或其颁发者)提供的证书,然后您才应该验证签名。

这样构建X509链

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

if (!success) throw Error

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

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

查看以下链接

Create JWK Set Containing Certificates

Generate x5c certificate chain from JWK

How to obtain JWKs and use them in JWT signing?

How to get x5c from RSACryptoServiceProvider

希望有用。

#编辑

如果问题是将x5c 提供给标题,您必须使用添加它

token.Header.Add(name, value)

【讨论】:

感谢您付出的努力,但问题不在于创建证书链。它是关于让 System.IdentityModel.Tokens.Jwt nuget 包(特别是 JwtSecurityTokenHandler 类)输出包含 x5c 标头参数的 JWT 令牌,而不仅仅是 x5t 标头参数。我将更新问题以更清楚地说明这一点。 SecurityTokenDescriptor.ClaimsSecurityTokenDescriptor.AdditionalHeaderClaims 目前仅支持 Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler. 在安装System.IdentityModel.Tokens.Jwt nuget 包时,还会得到Microsoft.IdentityModel.Tokens,这样就可以将SecurityTokenDescriptor 类与JwtSecurityTokenHandler 类一起使用。在SecurityTokenDescriptor.AdditionalHeaderClaims 中手动添加 x5c 似乎是正确的做法,但不幸的是没有输出“x5c”。似乎JwtSecurityTokenHandler 只是忽略了它。 很抱歉没有提供任何帮助 您对使用token.Header.Add(..)(现已删除)的建议非常棒!这解决了整个问题!如果您可以使用此代码 sn-p 更新您的答案,那么我将授予您赏金:pastebin.com/cBnkRFgu

以上是关于使用 System.IdentityModel.Tokens.Jwt 创建带有 x5c 标头参数的 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)