EncryptValue中的JwtSecurityTokenHandler NotSupportedException

时间:2017-12-06 19:18:55

标签: c# asp.net authentication jenkins jwt

我有一个JsonWebTokenFormat类,它创建一个JWT令牌并使用X.509 RSA SSH 256证书对其进行签名。

internal class JsonWebTokenFormat : ISecureDataFormat<AuthenticationTicket>
{
    private readonly string _issuer;
    private readonly ICertificateStore _store;

    public JsonWebTokenFormat(string issuer, ICertificateStore store)
    {
        _issuer = issuer;
        _store = store;
    }

    public string Protect(AuthenticationTicket data)
    {
        if (data == null)
        {
            throw new ArgumentNullException("data");
        }

        RSA rsaPrivateKey = _store.GetCurrentUserPrivateCertificate(_issuer);

        SigningCredentials signingCredentials = new SigningCredentials(new RsaSecurityKey(rsaPrivateKey), SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);

        DateTimeOffset? issued = data.Properties.IssuedUtc;
        DateTimeOffset? expires = data.Properties.ExpiresUtc;

        JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
            issuer: _issuer,
            claims: data.Identity.Claims,
            notBefore: issued.Value.UtcDateTime,
            expires: expires.Value.UtcDateTime,
            signingCredentials: signingCredentials);
        JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
        string jwtAuthToken = jwtSecurityTokenHandler.WriteToken(jwtSecurityToken);

        return jwtAuthToken;
    }

    public AuthenticationTicket Unprotect(string jwtToken)
    {
        // read the issuer from the token
        JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(jwtToken);
        RSA rsaPublicKey = _store.GetPublicCertificateForClient(jwtSecurityToken.Issuer);

        TokenValidationParameters tokenValidationParams = new TokenValidationParameters
        {
            ValidIssuer = _issuer,
            RequireExpirationTime = true,
            ValidateIssuer = true,
            RequireSignedTokens = true,
            ValidateLifetime = true,
            ValidateAudience = false,
            IssuerSigningKey = new RsaSecurityKey(rsaPublicKey),
            ValidateIssuerSigningKey = true
        };

        JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
        SecurityToken tempToken;
        ClaimsPrincipal principal = jwtSecurityTokenHandler.ValidateToken(jwtToken, tokenValidationParams, out tempToken);

        AuthenticationTicket authenticationTicket = new AuthenticationTicket(new ClaimsIdentity(principal.Identity), new AuthenticationProperties());

        return authenticationTicket;
    }
}

ICertificateStore实现如下:

class MockCertificateStore : ICertificateStore
{
    private readonly X509Certificate2 _certificate;

    public MockCertificateStore()
    {
        _certificate = new X509Certificate2(
                @"C:\certs\test-client.pfx",
                "12345",
                X509KeyStorageFlags.MachineKeySet |
                X509KeyStorageFlags.Exportable);
    }

    public RSA GetCurrentUserPrivateCertificate(string subject)
    {
        return _certificate.GetRSAPrivateKey();
    }

    public RSA GetPublicCertificateForClient(string clientId)
    {
        return _certificate.GetRSAPublicKey();
    }
}

所以我有这个测试这个类的单元测试,它在我的本地机器(以及其他开发人员和本地机器)上工作正常,但它在我们的Jenkins构建环境中失败了。

失败,出现以下异常:

Test method AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken threw exception: 
System.NotSupportedException: Method is not supported.
Stack Trace:
    at System.Security.Cryptography.RSA.DecryptValue(Byte[] rgb)
    at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
    at System.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Byte[] input) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\AsymmetricSignatureProvider.cs:line 224
    at System.IdentityModel.Tokens.JwtSecurityTokenHandler.CreateSignature(String inputString, SecurityKey key, String algorithm, SignatureProvider signatureProvider) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 854
    at System.IdentityModel.Tokens.JwtSecurityTokenHandler.WriteToken(SecurityToken token) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 815
    at AuthCore.Token.JsonWebTokenFormat.Protect(AuthenticationTicket data) in C:\Jenkins\workspace\AuthCore\Token\JsonWebTokenFormat.cs:line 38
    at AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken() in C:\Jenkins\workspace\AuthCore.Tests\Token\JsonWebTokenFormatTests.cs:line 34

感谢任何帮助。我看了一堆SO问题,但没有一个问题有帮助。

1 个答案:

答案 0 :(得分:0)

解决!

问题是自.NET 4.6.0以来,RsaSecurityKey类已被弃用。出于某种原因,这个类在没有安装旧版本.NET的计算机中使用时会抛出,但在使用旧版本.NET的计算机上却很好。相反,只需使用X509SecurityKey类。

有关可能的解决方案,请参阅以下文章: https://q-a-assistant.com/computer-internet-technology/338482_jwt-generation-and-validation-in-net-throws-key-is-not-supported.html

static string GenerateToken()
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var certificate = new X509Certificate2(@"Test.pfx", "123");
    var securityKey = new X509SecurityKey(certificate);

    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(),
        Issuer = "Self",
        IssuedAt = DateTime.Now,
        Audience = "Others",
        Expires = DateTime.MaxValue,
        SigningCredentials = new SigningCredentials(
            securityKey,
            SecurityAlgorithms.RsaSha256Signature)
    };

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

static bool ValidateToken(string token)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var certificate = new X509Certificate2(@"Test.cer");
    var securityKey = new X509SecurityKey(certificate);

    var validationParameters = new TokenValidationParameters
    {
        ValidAudience = "Others",
        ValidIssuer = "Self",
        IssuerSigningKey = securityKey
    };

    var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
    if (principal == null)
        return false;
    if (securityToken == null)
        return false;

    return true;
}