How to encrypt JWT security token?
Asked Answered
E

4

33

I need to secure my web-token with signing and encryption. I wrote the next lines of code:

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

So, I am using some certificates, generated with makecert.exe. Then I read token string with another JwtSecurityTokenHandler:

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

And token content is not encrypted (I can see json in tok variable under debugger). What am I doing wrong? How to encrypt token data?

Elvera answered 14/8, 2013 at 5:31 Comment(0)
B
8

My understanding is that Microsoft's JWT implementation doesn't currently support encryption (only signing).

Bots answered 14/8, 2013 at 6:11 Comment(6)
If so, I would really appreciate if you could provide me with some links on this topic.Vibraculum
I've explored this extension and looks like you're right - encryption is not yet supported. Thanks!Vibraculum
Is this still the case?Analgesic
As far as I know, yesBots
Is there any other way we can implement this functionality? Any libraries which can be used or may be overriding JwtSecurityTokenHandler?Duet
This issue is addressed in Microsoft.IdentityModel.Tokens version 5.1.3. See my answer for the detailsBerryman
B
57

I know this an old post, but I am adding my answer in case if someone is still searching for the answer.

This issue is addressed in Microsoft.IdentityModel.Tokens version 5.1.3. There is an overloaded method available in the CreateJwtSecurityToken function which accepts the encrypting credentials to encrypt the token.

If the receiver does not validate the signature and tries to read JWT as is then the claims are empty. Following is the code snippet:

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

And here is the code to validate/decrypt the token:

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);
Berryman answered 26/5, 2017 at 7:9 Comment(11)
how would i decrypt the token since there is no option of passing the key when validating the token?Samalla
@SangSuantak TokenValidationParameters has TokenDecryptionKey property. Validator internally uses this property to decrypt the token. I have updated my answer to include the decryption partBerryman
@Berryman can we use public key private key pair to encrypt and decrypt the JWT token. If yes, how.Softcover
Microsoft.IdentityModel is deprecated. Do you have an up-to-date example?Galley
@JacobStamm How does that page say that IdentityModel is deprecated? Only WCF is deprecated. WIF is not.Equatorial
Their GitHub repository has more information.Equatorial
@Berryman I assume this does sign-then-encrypt when creating tokens. Is there a way to encrypt-then-sign?Flexuous
@JacobStamm The current work for IdentityModel is here: github.com/AzureAD/… . We have several libraries that support asp.net core, you can find them on nuget.org.Rheotaxis
@StevenLiekens the concept of sign-then-encrypt or encrypt-then-sign is not critical for JWE's. The reason is only authenticated encryption algorithms are used. What occurs with JWE's, is that first a JWS is created specifying SigningCredentials, then the JWE is created by wrapping that JWS.Rheotaxis
@RajatAgrawal yes you can use Pub/Pri key pairs. I you use RSA keys then the libraries will create symmetricKey and wrap the key.Rheotaxis
@Berryman This procedure outputs the tokwn with "alg": "A128KW", "enc": "A128CBC-HS256" as the header claims. The "enc" parameter looks as expected, but shouldn't "alg" remain "HS256"?Estivate
G
18

Try the following example

Updated Jul-2019: .NET Core, Asp.net Core

1.Create 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.Add to ConfigureServices(IServiceCollection services) in Startup.cs

    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),
    };
});
Grandfatherly answered 8/7, 2019 at 11:45 Comment(1)
Can you analyze and help me the code to receive the Token from the request then decode and compare with the Data token stored? This code runs fine but I don't understand how it works, Thank you!Yim
B
8

My understanding is that Microsoft's JWT implementation doesn't currently support encryption (only signing).

Bots answered 14/8, 2013 at 6:11 Comment(6)
If so, I would really appreciate if you could provide me with some links on this topic.Vibraculum
I've explored this extension and looks like you're right - encryption is not yet supported. Thanks!Vibraculum
Is this still the case?Analgesic
As far as I know, yesBots
Is there any other way we can implement this functionality? Any libraries which can be used or may be overriding JwtSecurityTokenHandler?Duet
This issue is addressed in Microsoft.IdentityModel.Tokens version 5.1.3. See my answer for the detailsBerryman
M
3

Hung Quach's answer worked just fine for .NET 5 on Blazor for me. Although I changed it a bit to for my style.

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

then I am reading the token as follows :

 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;
Miculek answered 18/9, 2021 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.