将 SymmetricSecurityKey 交换为 JWT 的 AsymmetricSecurityKey

Swapping SymmetricSecurityKey for AsymmetricSecurityKey for JWT

渗透测试建议我们更改 JWT 实现以使用非对称签名而不是对称签名,后者运行良好。

创建对称令牌的当前(完美工作)代码如下:(灵感最初来自 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);

        }
    }

检查任何 API 调用请求的代码类似于以下内容:

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) { });
        }

我已经在网上尝试了很多示例并阅读了很多帖子,但还没有任何工作,每个都有一大堆不同的问题。可能我已经关闭的是以下一些示例:

下面是一个似乎在创建代码时起作用的示例,但在将令牌与传入的 API 调用进行比较时,这不会起作用。

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

最后,下面的选项感觉正确,但由于字符串中包含加密密钥内容,出现 'Bad Version of provider.' 错误。

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

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

我在使用各种选项时遇到困难。

找到解决方案,将上面的转换为非对称加密,交换:

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

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

加密密钥是使用以下生成的。

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

然后在检查传入的JWT时:

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

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

希望对以后的人有所帮助。