Not able to validate JSON Web token with .net - ke

2019-04-02 07:45发布

问题:

I have used JSONWebToken npm module to generate a jot:

var jwt = require('jsonwebtoken');

var payload = {
    "iss": "https://secure.example.com/",
    "exp": 1410819380,
    "http://example.com/orgnum": "987987987",
    "http://example.com/user": "me@example.com"
};

var token = jwt.sign(payload, 'secret');

console.log(token);

This gives me the following output:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NlY3VyZS5leGFtcGxlLmNvbS8iLCJleHAiOjE0MTA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9vcmdudW0iOiI5ODc5ODc5ODciLCJodHRwOi8vZXhhbXBsZS5jb20vdXNlciI6Im1lQGV4YW1wbGUuY29tIiwiaWF0IjoxNDA4Mzk0Mjk2fQ.5X5LTg4wxDF2p49xtsRcG4S9Yk4qSfW1tMEU0AquBhc

Since I'm not specifying what algorithm I want, it uses SHA256.

Now, I try to verify this in c#. That didn't turn out easy...

I get an exception about the key size:

IDX10603: The 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey' cannot have less than: '128' bits. Parameternavn: key.KeySize The actual size was 48.

I I try to extend the key, I get a new error when creating the symmetric key:

Invalid length for a Base-64 char array or string

I recon this has something to do with the way I'm telling the .net code about my key. Since the SymmetricKeyIssuerSecurityTokenProvider constructor parameter is named base64Key, I have tryed to Base64Url-encode my key:

        var secret =  Base64UrlEncoder.Encode("secret");
        TokenValidationParameters validationParameters = new TokenValidationParameters
        {                
            ValidateIssuer = false,
            ValidateAudience = false,
            IssuerSigningTokens = new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret).SecurityTokens
        };

So, what are I missing here?
Why can jsonwebtoken generate and validate jots with short keys while .net can not?
And why can't .net accept the keys I give it?

Here's the complete .net code with a jot signed with a long key:

        var jwtToken =
            "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NlY3VyZS5leGFtcGxlLmNvbS8iLCJleHAiOjE0MTA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9vcmdudW0iOiI5ODc5ODc5ODciLCJodHRwOi8vZXhhbXBsZS5jb20vdXNlciI6Im1lQGV4YW1wbGUuY29tIiwiaWF0IjoxNDA4Mzk1NjY4fQ.ZceiiEO_Mn5_GZp5D_r68VTT33fbocn1BTTznD6u3cs";
        var secret = Base64UrlEncoder.Encode("super duper secret with some more on top");


        TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                ValidateAudience = false,
                IssuerSigningTokens = new SymmetricKeyIssuerSecurityTokenProvider("issuer", secret).SecurityTokens
            };


        JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler()
            {
                Configuration = new SecurityTokenHandlerConfiguration()
                    {
                        CertificateValidationMode = X509CertificateValidationMode.None
                    }
            };

        SecurityToken validatedToken;
        var claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);
        return claimsPrincipal.Claims;

Updated:

I'm only using Microsoft-stuff in this code. I'm using the Owin packages Microsoft.Owin.Security.Jwt version 2.1.0 with System.IdentityModel.Tokens.Jwt version 4.0.0-RC2.

There are multiple blog posts out there stating that you'll need to manually update the System.IdentityModel.Tokens.Jwt package.

回答1:

I'm not sure what API you are using, since the official Microsoft one does not contain the properties that you are using. My guess would be, you are using an outdated version.

I took the API from this Nuget package. And this is the code, that worked for me:

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.ServiceModel.Security.Tokens;
using System.Text;

namespace SO25372035
{
    class Program
    {
        static void Main()
        {
            const string tokenString =  @"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NlY3VyZS5leGFtcGxlLmNvbS8iLCJleHAiOjE0MTA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9vcmdudW0iOiI5ODc5ODc5ODciLCJodHRwOi8vZXhhbXBsZS5jb20vdXNlciI6Im1lQGV4YW1wbGUuY29tIiwiaWF0IjoxNDA4NDE5NTQwfQ.jW9KChUTcgXMDp5CnTiXovtQZsN4X-M-V6_4rzu8Zk8";
            JwtSecurityToken tokenReceived = new JwtSecurityToken(tokenString);

            byte[] keyBytes = Encoding.UTF8.GetBytes("secret");
            if (keyBytes.Length < 64 && tokenReceived.SignatureAlgorithm == "HS256")
            {
                Array.Resize(ref keyBytes, 64);
            }
            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                AudienceUriMode = AudienceUriMode.Never,
                SigningToken = new BinarySecretSecurityToken(keyBytes),
            };

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(tokenReceived, validationParameters);
            IEnumerable<Claim> a = claimsPrincipal.Claims;
            foreach (var claim in a)
            {
                Console.WriteLine(claim);
            }
        }
    }
}

Note, that I had to resize the array containing the key so that key length passes the validation. It appears that they key length for HMAC is always equal to the block size, and for SHA256 it's 512 bits. There is MinimumSymmetricKeySizeInBits static property that defines the minimum length of a SimmetricKey, but it appears it can't be set to be less than 128.



回答2:

Try to use TextEncodings.Base64Url.Decode api from Microsoft.Owin.Security.Jwt package to decode the signing key

Then I do following to validate token:

var principal = new JwtSecurityTokenHandler().ValidateToken(jwtheader,
                        new TokenValidationParameters()
                        {
                            RequireExpirationTime = true,
                            ValidAudience = audience,
                            ValidIssuer = issuer,
                            IssuerSigningKey = new InMemorySymmetricSecurityKey(secret)
                        }, out token);


回答3:

Correct about the key not being less that 128 bits, since support is only for AES that is the minimum.

You can use your own SignatureProvider.

I also think you are using older bits. Have a look at the latest. http://www.nuget.org/packages/System.IdentityModel.Tokens.Jwt/

If you have any issues, let me know.