Swapping SymmetricSecurityKey for AsymmetricSecurityKey for JWT
Asked Answered
B

1

5

A penetration test has recommended that we change our JWT implementation to use asymmetric signing instead of symmetric signing, which is working well.

The current (perfectly working) code to Create the symmetric token is below: (inspiration originally taken from How to encrypt JWT security token?)

private string CreateToken(string Username)
        {
            //Set issued at date
            DateTime issuedAt = DateTime.UtcNow;
            //set the time when it expires
            DateTime expires = DateTime.UtcNow.AddHours(1);

            
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            //create a identity and add claims to the user which we want to log in
            ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, Username)
            });

            DateTime now = DateTime.UtcNow;
            SymmetricSecurityKey securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
            SigningCredentials signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

            //create the jwt
            JwtSecurityToken token = tokenHandler.CreateJwtSecurityToken(issuer: "issuer",
                                                                         audience: "audience",
                                                                         subject: claimsIdentity,
                                                                         notBefore: issuedAt,
                                                                         expires: expires,
                                                                         signingCredentials: signingCredentials);

            return tokenHandler.WriteToken(token);

        }
    }

The code to check the request of any API calls looks similar to the below:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpStatusCode StatusCode;
            string token;

            //determine whether a jwt exists or not
            if (!TryRetrieveToken(request, out token))
            {
                StatusCode = HttpStatusCode.Unauthorized;
                //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
                return base.SendAsync(request, cancellationToken);
            }

            try
            {
                DateTime now = DateTime.UtcNow;
                SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));

                SecurityToken securityToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                TokenValidationParameters validationParameters = new TokenValidationParameters()
                {
                    ValidAudience = "audience",
                    ValidIssuer = "issuer",
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    LifetimeValidator = this.LifetimeValidator,
                    IssuerSigningKey = SecurityKey
                };

                //extract and assign the user of the jwt
                Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
                HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

                return base.SendAsync(request, cancellationToken);
            }
            catch (SecurityTokenValidationException e)
            {
                StatusCode = HttpStatusCode.Forbidden;
            }
            catch (Exception ex)
            {
                StatusCode = HttpStatusCode.Forbidden;
            }
            return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(StatusCode) { });
        }

I've tried quite a few examples online and read quite a few posts, but haven't gotten anything working as yet, each with a whole host of different issues. Probably the closed I've got is following some of the examples at: RS256 vs HS256: What's the difference?

One example that seemed to work in creating the code is below, but this wouldn't have worked when comparing the token with incoming API calls.

                RSA _rsaa;
                _rsaa = new RSACryptoServiceProvider(2048);
                var r = _rsaa.ExportParameters(true);
                SigningCredentials signingCredentials3a = new SigningCredentials(new RsaSecurityKey(_rsaa), SecurityAlgorithms.RsaSha256Signature);

Finally the option below felt right, but was getting a 'Bad Version of provider.' error due to the encryption key contents held in the string.

            byte[] Key256Bytes = Encoding.ASCII.GetBytes(Key256);

                rsa.ImportCspBlob(Key256Bytes);
                SigningCredentials signingCredentials5 = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256Signature);

I'm having difficulty getting the various options working.

Baccarat answered 22/2, 2021 at 20:53 Comment(0)
B
11

Found the solution, to convert the above into Assymmetric encryption, swap:

SymmetricSecurityKey securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
SigningCredentials signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

with

var rsa = new RSACryptoServiceProvider(); 
rsa.ImportCspBlob(Convert.FromBase64String(SecurityConstants.ConstAsyncSecurityEncryptionKey));                
SigningCredentials signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha512Signature);

The encryption key was generated by using the below.

var rsa = new RSACryptoServiceProvider(2048);
var key = Convert.ToBase64String(rsa.ExportCspBlob(true));

Then when checking the incoming JWT:

SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));

With

var rsa = new RSACryptoServiceProvider();      
rsa.ImportCspBlob(Convert.FromBase64String(SecurityConstants.ConstAsyncSecurityEncryptionKey));
RsaSecurityKey SecurityKey = new RsaSecurityKey(rsa);

Hope that helps someone in the future.

Baccarat answered 23/2, 2021 at 16:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.