Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IDX10659 error when using X509Certficate to decrypt JWT token

I'm creating a web service and associated client that will use JWT encrypted by X509Certficates. When I use a symmetric key to encrypt and decrypt the tokens, everything works fine. However, I want to use encryption based on X509Certficates in production, and when I try one of those, I get these exceptions:

When running on .NET Core:

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

When running on .NET Framework 4.7 (note how the inner exception differs):

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

Switching from a SymmetricSecurityKey to a X509SecurityKey is just changing the value provided for an instance of EncryptingCredentials, so I was expecting everything to just work.

Here's my code to generate a token:

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);
}

And here's my code to verify the same token (I've commented the line where the exception is thrown):

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;
    }
}

To create the EncryptingCredentials based on a certificate:

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

These methods are taken from a minimal repro (GitHub link) that I've extracted from my original codebase; there's a bit much code to publish here, but most of it is just infrastructure. The repro works entirely in-process and doesn't use anything from ASP.NET.

I've reproduced the problem with multiple different X509Certficates, on three different machines, under two different user accounts, so I think I've eliminated the certificate as the source of the problem. Signing works properly from end to end in each test, which I think shows that both the public and private keys from the certificate are properly accessible.

Why does decryption fail only with an X509SecurityKey and not with a SymmetricSecurityKey?

What's the right way to encrypt/decrypt JWT tokens with X509Certficates? Or, what's a workaround for this problem?

Updated: Simplified the code for reproducing the exception

like image 944
Bevan Avatar asked Oct 30 '22 08:10

Bevan


1 Answers

Try using:

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

instead of X509SecurityKey.

There appears to be a bug in the implementation where it is not allowing JIT unwrapping of RSA keys.

like image 92
kiwidev Avatar answered Nov 15 '22 07:11

kiwidev