如何加密 JWT 安全令牌?

Posted

技术标签:

【中文标题】如何加密 JWT 安全令牌?【英文标题】:How to encrypt JWT security token? 【发布时间】:2013-08-15 22:35:35 【问题描述】:

我需要通过签名和加密来保护我的网络令牌。我写了下一行代码:

var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor

      Subject = new ClaimsIdentity(new[]
         
             new Claim(ClaimTypes.Name, owner.Name),
             new Claim(ClaimTypes.Role, owner.RoleClaimType),
             new Claim("custom claim type", "custom content")
         ),
      TokenIssuerName = "self",
      AppliesToAddress = "http://www.example.com",
      Lifetime = new Lifetime(now, now.AddSeconds(60 * 3)),
      EncryptingCredentials = new X509EncryptingCredentials(new X509Certificate2(cert)),
      SigningCredentials = new X509SigningCredentials(cert1)
;
var token = (JwtSecurityToken)tokenHandler.CreateToken(tokenDescriptor);            
var tokenString = tokenHandler.WriteToken(token);

所以,我正在使用一些由makecert.exe 生成的证书。然后我用另一个JwtSecurityTokenHandler读取令牌字符串:

var tokenHandlerDecr = new JwtSecurityTokenHandler();
var tok = tokenHandlerDecr.ReadToken(tokenString);

并且令牌内容没有加密(我可以在调试器下的tok变量中看到json)。我究竟做错了什么?如何加密令牌数据?

【问题讨论】:

【参考方案1】:

我知道这是一篇旧帖子,但如果有人仍在寻找答案,我会添加我的答案。

Microsoft.IdentityModel.Tokens version 5.1.3 解决了此问题。 CreateJwtSecurityToken 函数中有一个可用的重载方法,它接受加密凭据来加密令牌。

如果接收方未验证签名并尝试按原样读取 JWT,则声明为空。以下是代码sn-p:

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

const string sec = "ProEMLh5e_qnzdNUQrqdHPgp";
const string sec1 = "ProEMLh5e_qnzdNU";
var securityKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(sec));
var securityKey1 = new SymmetricSecurityKey(Encoding.Default.GetBytes(sec1)); 

var signingCredentials = new SigningCredentials(
    securityKey,
    SecurityAlgorithms.HmacSha512);

List<Claim> claims = new List<Claim>()

    new Claim("sub", "test"),
;

var ep = new EncryptingCredentials(
    securityKey1,
    SecurityAlgorithms.Aes128KW,
    SecurityAlgorithms.Aes128CbcHmacSha256);

var handler = new JwtSecurityTokenHandler();

var jwtSecurityToken = handler.CreateJwtSecurityToken(
    "issuer",
    "Audience",
    new ClaimsIdentity(claims),
    DateTime.Now,
    DateTime.Now.AddHours(1),
    DateTime.Now,
    signingCredentials,
    ep);


string tokenString = handler.WriteToken(jwtSecurityToken);

// Id someone tries to view the JWT without validating/decrypting the token,
// then no claims are retrieved and the token is safe guarded.
var jwt = new JwtSecurityToken(tokenString);

这是验证/解密令牌的代码:

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

const string sec = "ProEMLh5e_qnzdNUQrqdHPgp";
const string sec1 = "ProEMLh5e_qnzdNU";
var securityKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(sec));
var securityKey1 = new SymmetricSecurityKey(Encoding.Default.GetBytes(sec1));

// This is the input JWT which we want to validate.
string tokenString = string.Empty;

// If we retrieve the token without decrypting the claims, we won't get any claims
// DO not use this jwt variable
var jwt = new JwtSecurityToken(tokenString);

// Verification
var tokenValidationParameters = new TokenValidationParameters()

    ValidAudiences = new string[]
    
        "536481524875-glk7nibpj1q9c4184d4n3gittrt8q3mn.apps.googleusercontent.com"
    ,
    ValidIssuers = new string[]
    
        "https://accounts.google.com"
    ,
    IssuerSigningKey = securityKey,
    // This is the decryption key
    TokenDecryptionKey = securityKey1
;

SecurityToken validatedToken;
var handler = new JwtSecurityTokenHandler();

handler.ValidateToken(tokenString, tokenValidationParameters, out validatedToken);

【讨论】:

由于在验证令牌时没有传递密钥的选项,我将如何解密令牌? @SangSuantak TokenValidationParameters 具有 TokenDecryptionKey 属性。 Validator 在内部使用此属性来解密令牌。我已更新我的答案以包括解密部分 @Amey 我们可以使用公钥私钥对来加密和解密 JWT 令牌吗?如果是,如何。 Microsoft.IdentityModel is deprecated。你有最新的例子吗? @JacobStamm 该页面怎么说IdentityModel 已被弃用?只有 WCF 被弃用。 WIF 不是。【参考方案2】:

试试下面的例子

2019 年 7 月更新:.NET Core、Asp.net Core

1.创建JWT

private string CreateJwt(string sub, string jti, string issuer, string audience)

    var claims = new[]
    
        new Claim(JwtRegisteredClaimNames.Sub, sub),
        new Claim(JwtRegisteredClaimNames.Jti, jti),
    ;

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecretKeySecretKeySecretKeySecretKeySecretKeySecretKeySecretKeyS"));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    var encryptingCredentials = new EncryptingCredentials(key, JwtConstants.DirectKeyUseAlg, SecurityAlgorithms.Aes256CbcHmacSha512);

    var jwtSecurityToken = new JwtSecurityTokenHandler().CreateJwtSecurityToken(
        issuer,
        audience,
        new ClaimsIdentity(claims),
        null,
        expires: DateTime.UtcNow.AddMinutes(5),
        null,
        signingCredentials: creds,
        encryptingCredentials: encryptingCredentials
        );
    var encryptedJWT = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

    return encryptedJWT;

2.在Startup.cs中添加到ConfigureServices(IServiceCollection services)

    services.AddAuthentication(options =>

    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(options =>

    options.TokenValidationParameters = new TokenValidationParameters
    
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,

        ValidIssuer = (string)Configuration.GetSection("JwtToken").GetValue(typeof(string), "Issuer"),
        ValidAudience = (string)Configuration.GetSection("JwtToken").GetValue(typeof(string), "Audience"),
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecretKeySecretKeySecretKeySecretKeySecretKeySecretKeySecretKeyS")),
        TokenDecryptionKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecretKeySecretKeySecretKeySecretKeySecretKeySecretKeySecretKeyS")),
        ClockSkew = TimeSpan.FromMinutes(0),
    ;
);

【讨论】:

您能否分析并帮助我从请求中接收令牌然后解码并与存储的数据令牌进行比较?此代码运行良好,但我不明白它是如何工作的,谢谢!【参考方案3】:

我的理解是微软的 JWT 实现目前不支持加密(只支持签名)。

【讨论】:

如果是这样,如果您能提供一些关于此主题的链接,我将不胜感激。 我已经探索了这个扩展,看起来你是对的 - 尚不支持加密。谢谢! 还是这样吗? 我们有没有其他方法可以实现这个功能?任何可以使用或可能覆盖 JwtSecurityTokenHandler 的库? 此问题已在 Microsoft.IdentityModel.Tokens 版本 5.1.3 中得到解决。详情见我的回答【参考方案4】:

Hung Quach 的回答对我来说适用于 Blazor 上的 .NET 5。虽然为了我的风格我把它改了一点。

   var claims = new List<Claim>()
            
                new Claim(ClaimTypes.Name,userRequestDTO.Email)
            ;
            var secret = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_APISettings.SecretKey));
            var signingCredentials = new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
            var encryptionCredentials = new EncryptingCredentials(secret, JwtConstants.DirectKeyUseAlg, SecurityAlgorithms.Aes256CbcHmacSha512);
            var tokenOptions = new JwtSecurityTokenHandler().CreateJwtSecurityToken(new SecurityTokenDescriptor()
            
                Audience= _APISettings.ValidAudience,
                Issuer= _APISettings.ValidIssuer,
                Subject= new ClaimsIdentity(claims),
                Expires= DateTime.Now.AddMinutes(10),
                EncryptingCredentials= encryptionCredentials,
                SigningCredentials = signingCredentials
            );

            return new JwtSecurityTokenHandler().WriteToken(tokenOptions);

然后我正在读取令牌如下:

 JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
                var secret = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_APISettings.SecretKey));
                var signingCredentials = new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);

                tokenHandler.ValidateToken(tokenOTP,new TokenValidationParameters
                
                    ValidIssuer = _APISettings.ValidIssuer,
                    ValidAudience = _APISettings.ValidAudience,
                    ValidateIssuerSigningKey=true,
                    ValidateLifetime = true,
                    RequireExpirationTime = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    IssuerSigningKey= secret,
                    TokenDecryptionKey=secret,
                    ClockSkew = TimeSpan.Zero
                ,out SecurityToken validatedToken);
                var jwtToken = (JwtSecurityToken)validatedToken;

【讨论】:

以上是关于如何加密 JWT 安全令牌?的主要内容,如果未能解决你的问题,请参考以下文章

刷新令牌如何为 jwt 添加安全性?

加密 Nodejs JWT 令牌

将 WebAPI JWT 访问令牌作为加密的 FormsAuthenticationTicket 存储在 Response.Cookies 中是不是安全(在 asp.net mvc 中)

如何安全地存储和处理 JWT 的密钥

使用 JWT 刷新令牌如何安全?

使用 .NET Core 中的公共安全密钥配置 JWT Bearer 令牌验证