Shailen Sukul
Shailen Sukul

Reputation: 510

JWT - Decrypting a Token But Not Validating

My sample project

Summary

I have created a sample project to test the issuing of JWT tokens in an ASP.Net Core applications.

Refer to the Github repo above for the full sample.

In the JwtService.cs class, there is a method called GenerateSecurityToken, which generates the Jwt token.

The Login method of the AccountController calls the JwtService class to generate the token, saves the token in a cookie and also sets another cookie for the user id.

Note: The token is signed with a secret, which is also appended with a user specific salt. The user salt can be invalidated at any point, making the user's token invalid.

The token is also encrypted with an encryption key. This makes the body of the token illegible when inspected in JWT.io. I believe it is called a JWE.

This question discusses the signing and encrypting order.

Issue

I do not want the bearer or any third party to inspect the content of the token. The token's purpose in authentication.

My sample code intercepts the authentication pipeline, and uses the email from the cookie to pull user roles out of the database and creates role claims out of the roles.

A few of you may have figured out the information leak caused by issuing a separate cookie with the email.

Ideally, I want to ONLY issue the JWE token in the cookie.

I want to intercept the authentication pipeline, decrypt the token, use the email claim to get the user salt (from the db) and validate the token.

I have read the documentation, but cannot figure out a way to decrypt the token.

To be honest, I am not even sure of the order of operation (sign then encrypt or encrypt then sign), when the token is issued.

If anyone could even point me to the source code for JwtSecurityTokenHandler that might be a good start.

TIA

        public string GenerateSecurityToken(string email, byte[] salt, string[] roles)
        {
            var tokenHandler = new JwtSecurityTokenHandler();

            var key = Encoding.ASCII.GetBytes(_secret).Concat(salt).ToArray();
            byte[] ecKey = new byte[256 / 8];
            Array.Copy(Encoding.ASCII.GetBytes(_ecKey), ecKey, 256 / 8);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = _issuer,
                Audience = _audience,
                Subject = new ClaimsIdentity(
                    new List<Claim>
                    {
                        new Claim(ClaimTypes.Email, email)
                    }),
                    // .Concat(roles.Select(r => new Claim(ClaimTypes.Role, r))).ToArray()),
                Expires = DateTime.UtcNow.AddMinutes(double.Parse(_expDate)),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
                EncryptingCredentials = new EncryptingCredentials(
        new SymmetricSecurityKey(
            ecKey),
            SecurityAlgorithms.Aes256KW,
            SecurityAlgorithms.Aes256CbcHmacSha512)
            };

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

Upvotes: 1

Views: 1501

Answers (1)

Useme Alehosaini
Useme Alehosaini

Reputation: 3116

While you are very interested in hashing the JWT, I recommend you have four methods:

To generates the token (Sequence is important):

  1. GenerateToken
  2. HashToken

Then when you receive the bearer token with the request (Sequence is important):

  1. DeHashToken
  2. ValidateToken

As both processes, JWT generating and hashing are very straightforward:

To get the best results, forget about the hashing and focus on Generate Token and Validate Token.

After you get success with JWT, in a separate project, focus on Hashing and DeHashing any text (JWT or something else).

When you succeed with both, don't call each other from each other.

Build new Methods like

  1. GenerateAndHash (It calls both GenerateToken and HashToken)
  2. VerfyHashAndValidateToken (It calls both DeHashToken and ValidateToken)

To Generate and Validate JWT step by step see this (The title of the article could be confusing but it talks about JWT in .NET Core in a very simple way especially in part 2 I think)

https://www.codemag.com/Article/1805021/Security-in-Angular-Part-1

https://www.codemag.com/Article/1809031/Security-in-Angular-Part-2

https://www.codemag.com/Article/1811031/Security-in-Angular-Part-3

To Hash

public static string ToCustomHash(this string text)
            {
                byte[] salt;
                new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
    
                var pbkdf2 = new Rfc2898DeriveBytes(text, salt, 100000);
                byte[] hash = pbkdf2.GetBytes(20);
    
                byte[] hashBytes = new byte[36];
                Array.Copy(salt, 0, hashBytes, 0, 16);
                Array.Copy(hash, 0, hashBytes, 16, 20);
    
                var hashedToBase64 = Convert.ToBase64String(hashBytes);
    
                return hashedToBase64;
            }

To Verify The hash

public static bool VerifyHashWith(this string storedPassword, string loginPassword)
        {
            byte[] hashBytes = Convert.FromBase64String(storedPassword);
    
            byte[] salt = new byte[16];
    
            Array.Copy(hashBytes, 0, salt, 0, 16);
    
            var pbkdf2 = new Rfc2898DeriveBytes(loginPassword, salt, 100000);
    
            byte[] hash = pbkdf2.GetBytes(20);
    
            for (int i = 0; i < 20; i++)
            {
                if (hashBytes[i + 16] != hash[i]) { return false; }
            }
    
            return true;
        }

Upvotes: 1

Related Questions