VerifyJWT using a token generated from .NET, System.IdentityModel.Tokens.Jwt

Not applicable

Hi all,

I have been trying to configure my policy to validate an external JWT, but it is failing the validation saying

"The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA256".

I have tested the JWT token on JWT.IO and all looks okay there.

I am at a loss! I wish Apigee would have told me more in the error message.

I am creating JWT using HS256 algorithm, using Microsoft's System.IdentityModel.Tokens.Jwt .Net library.

For example, this JWT has been created by the same algorithm:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Im1pcyIsIm5iZiI6MTUyNDU1NzkyNCwiZXhwIjoxNTUzNTg4MzI0LCJpYXQiOjE1MjQ1NTc5MjR9.7YjJNl2b4f8J6zoVETw9kQnO
C6W9FxZQTECL9mXzgBE

The above JWT will be valid for a few days, if someone can try this out.

Help! Please let me know if you want more information.

This is my first message on these forums, so please help me provide more information in this context.

Calling out to @Omid Tahouri, @Dino.

Regards,

Rahul Kumar

Solved Solved
0 7 10.1K
1 ACCEPTED SOLUTION

I've looked and it works for me here. When using .NET libraries to create a HS256 token, I can validate the token using the Apigee Edge policy.

The .NET code I used to generate the token is like this:

      void Run() {
        var now = DateTime.UtcNow;
        var securityKey = new Microsoft.IdentityModel.Tokens.
          SymmetricSecurityKey(Encoding.UTF8.GetBytes(_passphrase));
    
        var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "HS256");
        var header = new JwtHeader(signingCredentials);
        var payload = new JwtPayload
        {
          { "unique_name", _name },
          { "scope", "https://apigee.com/example"},
          { "iat", RenderAsEpoch(now)},
          { "nbf", RenderAsEpoch(now)},
          { "exp", RenderAsEpoch(now.AddSeconds(_expiry))}
        };
        var secToken = new JwtSecurityToken(header, payload);
        var handler = new JwtSecurityTokenHandler();
        var tokenString = handler.WriteToken(secToken);
        Console.WriteLine("\ntoken:\n" + tokenString);
        
        var decodedToken = handler.ReadToken(tokenString);
        Console.WriteLine("\nDecoded: \n"+ decodedToken);
      }

Full code here.

This is my policy configuration.

<VerifyJWT name="Verify-JWT-HS256-BasicNoAudience">
    <Algorithm>HS256</Algorithm>
    <Source>inbound.jwt</Source>
    <SecretKey>
        <Value ref="private.secretkey"/>
    </SecretKey>
</VerifyJWT>

Initially I saw a verification failure. Upon further review, I had a mistake in the policy configuration. I had an incorrect value in the Secret key. When I verified the key was what I thought it was, it worked just fine.

View solution in original post

7 REPLIES 7

Hi Rahul,

are you still experiencing problems? I've been away from my desk and haven't answered questions for a few days.

If so, please post the configuration of your policy. You wrote:

I have been trying to configure my policy to validate an external JWT,...

But you didn't show the configuration you tried.

The JWT you posted looks fine. To validate it within Apigee , you need to configure the VerifyJWT policy with the correct algorithm and Secret Key. If you are using the Java callout policy, then it's a different configuration, but you didn't specify one way or the other which policy you're using!

Hi Dino,

Thanks for responding!

This is the JWT validation policy I am using:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWT async="false" continueOnError="false" enabled="true" name="Verify-JWT-1">
    <DisplayName>Verify JWT-1</DisplayName>
    <Algorithm>HS256</Algorithm>
    <Source>mis.jwt</Source>
    <SecretKey>
        <Value ref="private.secret"/>
    </SecretKey>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</VerifyJWT>

The two variables are self-explanatory.

I have checked their values are correctly picked.

I hope this answers your question?

Please let me know if there anything else I need to provide.

ok let me look. I haven't generated tokens with the .NET class. I'll try that and see if I can get a sample that works.

I've looked and it works for me here. When using .NET libraries to create a HS256 token, I can validate the token using the Apigee Edge policy.

The .NET code I used to generate the token is like this:

      void Run() {
        var now = DateTime.UtcNow;
        var securityKey = new Microsoft.IdentityModel.Tokens.
          SymmetricSecurityKey(Encoding.UTF8.GetBytes(_passphrase));
    
        var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "HS256");
        var header = new JwtHeader(signingCredentials);
        var payload = new JwtPayload
        {
          { "unique_name", _name },
          { "scope", "https://apigee.com/example"},
          { "iat", RenderAsEpoch(now)},
          { "nbf", RenderAsEpoch(now)},
          { "exp", RenderAsEpoch(now.AddSeconds(_expiry))}
        };
        var secToken = new JwtSecurityToken(header, payload);
        var handler = new JwtSecurityTokenHandler();
        var tokenString = handler.WriteToken(secToken);
        Console.WriteLine("\ntoken:\n" + tokenString);
        
        var decodedToken = handler.ReadToken(tokenString);
        Console.WriteLine("\nDecoded: \n"+ decodedToken);
      }

Full code here.

This is my policy configuration.

<VerifyJWT name="Verify-JWT-HS256-BasicNoAudience">
    <Algorithm>HS256</Algorithm>
    <Source>inbound.jwt</Source>
    <SecretKey>
        <Value ref="private.secretkey"/>
    </SecretKey>
</VerifyJWT>

Initially I saw a verification failure. Upon further review, I had a mistake in the policy configuration. I had an incorrect value in the Secret key. When I verified the key was what I thought it was, it worked just fine.

That worked perfectly! Thanks for your help @Dino! Really appreciated.

I was creating the token differently. Although the JWT produced was valid, it wasn't validating correctly at Apigee. Just for reference, here was my token generation code, if you can spot something:

public static string GenerateToken(string username, int expireMinutes = 20)
        {
            var symmetricKey = Convert.FromBase64String(Secret);
            var tokenHandler = new JwtSecurityTokenHandler();


            var now = DateTime.UtcNow;
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[]
                {
                    new Claim( ClaimTypes.UserData,
                    "IsValid", ClaimValueTypes.String, "(local)" )
                }),
                Issuer = "self",
                Audience = "https://www.avengers.co.uk",
                Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256),
            };


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


            return token;
        }

Yes - it's hard to know for sure, but I think it's probable that the symmetricKey used in your .NET program is different from the symmetric (Secret) key you are using in the Apigee Edge policy.

The Convert.FromBase64String() function returns a byte[] from a string that is in base64 format. You haven't showed me that string (that's a good thing). But I'm wondering, how do you perform the similar conversion in the Apigee Edge policy? There are ways to do base64-conversions in Apigee Edge, but you didn't mention them. So maybe you missed that step?

The way I've set up my .NET code and policy in the answer above is to use a passphrase and just get the UTF-8 bytes from that. The key is the password. Using a passphrase as a key illustrates the point, proves that a JWT created in a .NET app can be verified by Apigee Edge. We knew that would be possible, anyway, because JWT is a simple standard, but it's nice to see it working.

But If you know anything about crypto, you know that it's not a good idea, for a production system to use the password as the key. It works well enough, but it's not what you would consider "secure". For more on why, see here or here. Basically, using a direct encoding of the passphrase makes hacks too easy.

When doing encryption using a passphrase, to make it harder to hack, the industry has settled on the concept of "password-based key derivation", by which an encryption key is derived from a password, but the key is not the password. There is an acronym (so you know it's legit): PBKDF2. And there's even an RFC defining how to do it - RFC 2898. .NET has a built in function for deriving a key this way: Rfc2898DeriveBytes. With Java, it's a little harder, but still pretty easy.

Hi @Dino,

Thanks for the great response once again. You are correct - I didn't perform the step of decoding the base64 string on Apigee. That must be the problem. I will give that a shot.

Just to conclude the discussion, this was my way of creating the key:

       var hmac = new HMACSHA256();
       var key = Convert.ToBase64String(hmac.Key);