Adam
Adam

Reputation: 4780

SignatureVerificationFailedException in JwtAuthForWebAPI

I have wired up the JwtAuthForWebAPI nuget project but I am not able to validate the generated tokens. I end up getting a 500 error. I am using the exact same key value for both token generation and also when configuring JwtAuthenticationMessageHandler.

This is the code to generate a token:

var tokenHandler = new JwtSecurityTokenHandler();
var symmetricKey = JsonWebTokenSecretKey.GetBytes();
var now = DateTime.UtcNow;

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(
        new[]{
            new Claim(JwtClaimKeys.Audience, SessionManager.Current.ApplicationId.ToString()), 
            new Claim(JwtClaimKeys.Subject, userLoginRequest.ApplicationInstanceId.ToString())
        }),
    TokenIssuerName = "My Company",
    Lifetime = new Lifetime(now, now.AddMinutes(tokenLifetimeInMinutes)),
    SigningCredentials = new SigningCredentials(
        new InMemorySymmetricSecurityKey(symmetricKey),
        "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
        "http://www.w3.org/2001/04/xmlenc#sha256")
};

tokenDescriptor.Subject.AddClaims(GetRoles(userLoginRequest));

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

This is the code to register the authentication handler:

var keyBuilder = new SecurityTokenBuilder();

var jwtHandler = new JwtAuthenticationMessageHandler
{
    Issuer = "My Company",
    AllowedAudience = ApplicationId.ToString(),
    SigningToken = keyBuilder.CreateFromKey(JsonWebTokenSecretKey),
    PrincipalTransformer = new MyUserPrincipleTransformer()
};

config.MessageHandlers.Add(jwtHandler);

This is the error I get:

{"Message":"An error has occurred.","ExceptionMessage":"IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey\r\n'.\nExceptions caught:\n ''.\ntoken: '{\"typ\":\"JWT\",\"alg\":\"HS256\"}.{\"aud\":\"1\",\"sub\":\"3\",\"role\":[\"User\",\"Admin\"],\"iss\":\"My Company\",\"exp\":1429547369,\"nbf\":1429543769}'","ExceptionType":"System.IdentityModel.SignatureVerificationFailedException",
"StackTrace":"   
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)\r\n   
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)\r\n   
at JwtAuthForWebAPI.JwtSecurityTokenHandlerAdapter.ValidateToken(IJwtSecurityToken securityToken, TokenValidationParameters validationParameters)\r\n   
at JwtAuthForWebAPI.JwtAuthenticationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n   
at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()"}

This is an example JSON token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwic3ViIjoiMyIsInJvbGUiOlsiVXNlciIsIkFkbWluIl0sImlzcyI6Ik15IENvbXBhbnkiLCJleHAiOjE0Mjk1NTE4MjgsIm5iZiI6MTQyOTU0ODIyOH0.9wA_RBir9u7Cn_-Fy2T-Q_IDUfz6B928IEbIgXD9Bug

Interestingly, I am able to validate the token with my key using http://jwt.io. I suspect it may have something to do with the JwtAuthForWebAPI library looking at something different than what the System.Identity JWT library is generating?

Upvotes: 5

Views: 7326

Answers (2)

Amirreza
Amirreza

Reputation: 663

it's just my code sample base on @Jamie answer

 protected string GetUsername(string token)
        {
            string secret = "keyyyyy!@3";
           var key = Convert.FromBase64String(secret);

            var IssuerSigningKey = new SymmetricSecurityKey(key);
            IdentityModelEventSource.ShowPII = true;
            var SigningCredentials = new SigningCredentials(
                     IssuerSigningKey,
                     SecurityAlgorithms.HmacSha256Signature);

            var handler = new JwtSecurityTokenHandler();
            var tokenSecure = handler.ReadToken(token) as SecurityToken;
            var validations = new TokenValidationParameters
            {

                ValidateIssuerSigningKey = true,
                IssuerSigningKey = IssuerSigningKey,
                ValidateIssuer = false,
                ValidateAudience = false
            };
            var claims = handler.ValidateToken(token, validations, out tokenSecure);
            return claims.Identity.Name;
        }

Upvotes: 0

Jamie
Jamie

Reputation: 141

this is Jamie (author of the JwtAuthForWebAPI package). The server config code - specifically, SecurityTokenBuilder.CreateFromKey(string) - assumes the given string is base64 encoded. It was either that, or assumptions or parameters are needed that would indicate which encoding to use for converting to a byte array. I chose to assume the string was base64 encoded. I'm sure there's a clearer way to go about converting the string key into a SecurityToken, but that's the way the code is today.

In SmokeTests.cs within the JwtAuthForWebAPI.SampleClient project, you can see that I used the Convert.FromBase64String() method, as opposed to using the GetBytes() method from an Encoding class:

public const string SymmetricKey = "YQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6ADAAMQAyADMANAA1AA==";

// ...

var key = Convert.FromBase64String(SymmetricKey);
var credentials = new SigningCredentials(
    new InMemorySymmetricSecurityKey(key),
    "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
    "http://www.w3.org/2001/04/xmlenc#sha256");

Feel free to keep using your current token generation code, but on the server...

Please try specifying a base64 encoded version of JsonWebTokenSecretKey in the server configuration code. You can use a site like https://www.base64encode.org/ to encode it, or try code like this:

var base64key = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonWebTokenSecretKey));

var keyBuilder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
    Issuer = "My Company",
    AllowedAudience = ApplicationId.ToString(),
    SigningToken = keyBuilder.CreateFromKey(base64key),
    PrincipalTransformer = new MyUserPrincipleTransformer()
};

Let me know whether or not that works.

Also, I'm going to update the library to catch the SignatureVerificationFailedException exception and return a 401, as opposed to letting an internal server error happen. You'll still need to specify your key as a base64 string, but at least such configuration issues won't cause a 500 error.

Again, please let me know if that does the trick.

Upvotes: 7

Related Questions