使用 X509Certficate 解密 JWT 令牌时出现 IDX10659 错误

IDX10659 error when using X509Certficate to decrypt JWT token

我正在创建一个 Web 服务和关联的客户端,它将使用由 X509Certficates 加密的 JWT。当我使用对称密钥加密和解密令牌时,一切正常。但是,我想在生产中使用基于 X509Certficates 的加密,当我尝试其中之一时,出现以下异常:

在 .NET Core 上 运行 时:

Microsoft.IdentityModel.Tokens.SecurityTokenKeyWrapException: 
IDX10659: UnwrapKey failed, exception from crypto operation: 
'Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: 
Key does not exist'

在 .NET Framework 4.7 上 运行 时(注意内部异常有何不同):

Microsoft.IdentityModel.Tokens.SecurityTokenKeyWrapException: 
IDX10659: UnwrapKey failed, exception from crypto operation: 
'System.Security.Cryptography.CryptographicException: 
Error occurred while decoding OAEP padding.'

SymmetricSecurityKey 切换到 X509SecurityKey 只是更改为 EncryptingCredentials 的实例提供的值,所以我希望一切正常。

这是我生成令牌的代码:

public string Generate(NodeEntitlements entitlements)
{
    if (entitlements == null)
    {
        throw new ArgumentNullException(nameof(entitlements));
    }

    var claims = CreateClaims(entitlements);
    var claimsIdentity = new ClaimsIdentity(claims);

    var securityTokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = claimsIdentity,
        NotBefore = entitlements.NotBefore.UtcDateTime,
        Expires = entitlements.NotAfter.UtcDateTime,
        IssuedAt = DateTimeOffset.Now.UtcDateTime,
        Issuer = "https://example.com",
        Audience = "https://example.com",
        SigningCredentials = SigningCredentials,
        EncryptingCredentials = EncryptingCredentials
    };

    var handler = new JwtSecurityTokenHandler();
    var token = handler.CreateToken(securityTokenDescriptor);
    return handler.WriteToken(token);
}

这是我验证相同标记的代码(我已经注释掉抛出异常的行):

public VerificationResult Verify(string tokenString)
{
    var validationParameters = new TokenValidationParameters
    {
        ValidateAudience = true,
        ValidAudience = "https://example.com",
        ValidateIssuer = true,
        ValidIssuer = "https://example.com",
        ValidateLifetime = true,
        RequireExpirationTime = true,
        RequireSignedTokens = SigningKey != null,
        ClockSkew = TimeSpan.FromSeconds(60),
        IssuerSigningKey = SigningKey,
        ValidateIssuerSigningKey = SigningKey != null,
        TokenDecryptionKey = EncryptionKey
    };

    try
    {
        var handler = new JwtSecurityTokenHandler();

        // Exception is thrown here
        var principal = 
            handler.ValidateToken(tokenString, validationParameters, out var token);

        var entitlementIdClaim = principal.FindFirst("id");
        if (entitlementIdClaim == null)
        {
            return VerificationResult.IdentifierNotPresent;
        }

        return VerificationResult.Valid;
    }
    catch (SecurityTokenException ex)
    {
        Console.WriteLine(ex);
        return VerificationResult.InvalidToken;
    }
}

要根据证书创建 EncryptingCredentials

public static EncryptingCredentials CreateCertificateEncryptionCredentials()
{
    var certificate = FindCertificate();
    var key = new X509SecurityKey(certificate);
    var result = new EncryptingCredentials(
        key, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512);
    return result;
}

这些方法取自我从原始代码库中提取的 minimal repro (GitHub link);这里要发布的代码有点多,但其中大部分只是基础设施。 repro 完全在进程中工作,不使用 ASP.NET.

中的任何内容

我已经在三台不同的机器上,在两个不同的用户帐户下用多个不同的 X509Certficates 重现了这个问题,所以我认为我已经消除了证书作为问题的根源。签名在每个测试中从头到尾都正常工作,我认为这表明证书中的 public 和私钥都可以正确访问。

为什么解密只有 X509SecurityKey 而不是 SymmetricSecurityKey

使用 X509Certficates encrypt/decrypt JWT 令牌的正确方法是什么? 或者,这个问题的解决方法是什么?

更新:简化了重现异常的代码

尝试使用:

new RsaSecurityKey(certificate.GetRSAPrivateKey().ExportParameters(true));

而不是 X509SecurityKey

实现中似乎存在错误,它不允许 JIT 解包 RSA 密钥。