dnx451 RC1 What happened to InMemorySymmetricSecurityKey? - c#

I've been trying to create and sign a JwtSecurityToken using a simple key. And after a lot of research it seems that all the examples I find use the InMemorySymmetricSecurityKey class but unfortunately this class doesn't seem to exist in the newest versions of the System.IdentityModel libraries.
These are the dependencies I'm using:
"System.IdentityModel.Tokens": "5.0.0-rc1-211161024",
"System.IdentityModel.Tokens.Jwt": "5.0.0-rc1-211161024"
I also tried using it's base class SymmetricSecurityKey but then I get the following exception when trying to create the token:
"Value cannot be null.\r\nParameter name: IDX10000: The parameter 'signatureProvider' cannot be a 'null' or an empty object."
This is the code that throws the exception:
public static string CreateTokenHMAC()
{
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String("test"));
var key = new SymmetricSecurityKey(hmac.Key);
var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
JwtSecurityToken token = _tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor()
{
Audience = AUDIENCE,
Issuer = ISSUER,
Expires = DateTime.UtcNow.AddHours(6),
NotBefore = DateTime.Now,
Claims = new List<Claim>()
{
new Claim(ClaimTypes.Email, "johndoe#example.com")
},
SigningCredentials = signingCredentials
});
return _tokenHandler.WriteToken(token);
}
It's the first time I'm using JwtSecurityToken so my guess is that I'm probably missing a step somewhere

I was unable to get it to work using the RsaSecurityKey example provided in the accepted answer, but this did work for me (using System.IdentityModel.Tokens.Jwt v5.1.3).
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("test"));
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var securityTokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, "johndoe#example.com"),
new Claim(ClaimTypes.Role, "Administrator"),
}, "Custom"),
NotBefore = DateTime.Now,
SigningCredentials = signingCredentials,
Issuer = "self",
IssuedAt = DateTime.Now,
Expires = DateTime.Now.AddHours(3),
Audience = "http://my.website.com"
};
var tokenHandler = new JwtSecurityTokenHandler();
var plainToken = tokenHandler.CreateToken(securityTokenDescriptor);
var signedAndEncodedToken = tokenHandler.WriteToken(plainToken);
and to verify
var validationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidAudience = "http://my.website.com",
ValidateIssuer = true,
ValidIssuer = "self",
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
try
{
SecurityToken mytoken = new JwtSecurityToken();
var myTokenHandler = new JwtSecurityTokenHandler();
var myPrincipal = myTokenHandler.ValidateToken(signedAndEncodedToken, validationParameters, out mytoken);
} catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Authentication failed");
}

This should work (note this requires RC2 packages > 304180813)
var handler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
new Claim[] { new Claim(ClaimTypes.NameIdentifier, "bob") }),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(new byte[256]),
SecurityAlgorithms.HmacSha256)
};
var jwt = handler.CreateEncodedJwt(tokenDescriptor);

I managed to reach the exact same exception. I worked around the problem by generating the key another way:
RSAParameters keyParams;
using (var rsa = new RSACryptoServiceProvider(2048))
{
try
{
keyParams = rsa.ExportParameters(true);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
RsaSecurityKey key = new RsaSecurityKey(keyParams);
var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
Here is a great post about token-based authentication on ASP.NET 5 RC1 by Mark Hughes

Related

.NET Core JWE: no "cty" header

I'm using the following code to issue my JWEs:
var signCreds = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:SigningKey"])), SecurityAlgorithms.HmacSha256);
var encryptionCreds = new EncryptingCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Encryptionkey"])), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.CreateJwtSecurityToken(
Configuration["Jwt:Issuer"],
Configuration["Jwt:Audience"],
new ClaimsIdentity(claims),
DateTime.UtcNow,
expiresIn,
DateTime.UtcNow,
signCreds,
encryptionCreds);
But it doesn't specify "cty" header of the token - just only alg, enc and typ. If I understand correctly, the header must be set for encrypted JWT so I have an issue while parsing the token in golang because of the headers absence.
I also tried the following ways to issue JWE:
var signCreds = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:SigningKey"])), SecurityAlgorithms.HmacSha256);
var encryptionCreds = new EncryptingCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Encryptionkey"])), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var handler = new JwtSecurityTokenHandler();
var tokenDescriptor1 = new SecurityTokenDescriptor
{
Audience = "you",
Issuer = "me",
Subject = new ClaimsIdentity(claims),
EncryptingCredentials = encryptionCreds
};
var tokenDescriptor2 = new SecurityTokenDescriptor
{
Audience = "you",
Issuer = "me",
Subject = new ClaimsIdentity(claims),
EncryptingCredentials = encryptionCreds,
SigningCredentials = signCreds
};
var tokenDescriptor3 = new SecurityTokenDescriptor
{
Audience = "you",
Issuer = "me",
Subject = new ClaimsIdentity(claims),
EncryptingCredentials = encryptionCreds,
SigningCredentials = signCreds,
AdditionalHeaderClaims = new Dictionary<string, object> { { "cty", "JWT" } }
};
var enc = handler.CreateEncodedJwt(tokenDescriptor1);
var encSigned = handler.CreateEncodedJwt(tokenDescriptor2);
var encSignedWithCty = handler.CreateEncodedJwt(tokenDescriptor3);
But have the same result:
I scanned the library but have not found the code that set the Cty header for token.
Maybe anyone knows what I missed or what is the problem?
Thanks!
It seems to be a library issue

Sending post request with Authorization header and token returns unauthorized

I am trying to use jwt as my token and use it to access an authorized API. I use Postman to test my API and add Authorization header with the value Bearer MyToken but the server response is 401 UnAuthorized.
here is how I create my token:
in my startUp:
services.AddAuthentication (JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer (options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey (Encoding.ASCII
.GetBytes (Configuration.GetSection ("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
app.UseAuthentication ();
I have put the [Authorize]on the top of my Controller. and here is the part where I create my token:
class JWTToken {
public static object CreateToken (string Guid) {
var claims = new [] { new Claim (ClaimTypes.NameIdentifier, Guid) };
var key = new SymmetricSecurityKey (Encoding.UTF8.GetBytes ("Super secret key"));
var creds = new SigningCredentials (key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor {
Subject = new ClaimsIdentity (claims), Expires = DateTime.Now.AddYears (2), SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler ();
var token = tokenHandler.CreateToken (tokenDescriptor);
return tokenHandler.WriteToken(token);
}
You use different encodings:
// Here you use ASCII
IssuerSigningKey = new SymmetricSecurityKey (Encoding.ASCII
.GetBytes (Configuration.GetSection ("AppSettings:Token").Value))
// Here you use UTF8
var key = new SymmetricSecurityKey (Encoding.UTF8.GetBytes ("Super secret key"));
Also make sure that yours Configuration.GetSection ("AppSettings:Token").Value is same as "Super secret key" that you use to create JWT.
EDIT:
This is my configuration that works:
// In ConfigureServices
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Token:SigningKey"]));
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(config =>
{
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = signingKey,
ValidateAudience = true,
ValidAudience = this.Configuration["Token:Audience"],
ValidateIssuer = true,
ValidIssuer = this.Configuration["Token:Issuer"],
RequireExpirationTime = true,
RequireSignedTokens = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromMinutes(3)
};
});
// In token controller
private string GetToken(AppUser user)
{
var utcNow = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, utcNow.ToString())
};
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Token:SigningKey"]));
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
signingCredentials: signingCredentials,
claims: claims,
notBefore: utcNow,
expires: utcNow.AddSeconds(_configuration.GetValue<int>("Token:Lifetime")),
audience: _configuration["Token:Audience"],
issuer: _configuration["Token:Issuer"]
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
Maybe it will help you.
You need to validate the token signature. You set ValidateIssuersigningKey to true, but did you assign proper key to validate it? You could also try to implement custom validation method.
Middleware, if it considers that token is not valid, will respond with 401 as it cannot be authorized (Unauthotized)

How to implement ISecureDataFormat<AuthenticationTicket> Unprotect method?

Background:
I am attempting to support an authorization code flow to enable SSO from my app to a third party application.
Now I am stuck on the implementation of the Unprotect method which is supposed to return an AuthenticationTicket.
OAuth2 Server Configuration:
var allowInsecureHttp = bool.Parse(ConfigurationManager.AppSettings["AllowInsecureHttp"]);
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = allowInsecureHttp,
TokenEndpointPath = new PathString("/oauth2/token"),
AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(HlGlobals.Kernel),
AccessTokenFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel),
AuthorizationCodeProvider = new SimpleAuthenticationTokenProvider(),
AuthorizationCodeFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel)
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
JWT Token Generation / Protect method:
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
// Get the client and assign to GUID -- the audience is api this token will be valid against
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
Guid clientId;
bool isValidAudience = Guid.TryParse(audienceId, out clientId);
// Check for a valid Client Guid in the Auth ticket properties
if (!isValidAudience)
{
throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
}
// Create the JWT token
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
// Return the JWT Token
return jwt;
}
Finally, the 'Unprotect' method which is responsible for validation of the JWT and returning and authentication ticket:
public AuthenticationTicket Unprotect(string protectedText)
{
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
Guid clientId;
bool isValidAudience = Guid.TryParse(audienceId, out clientId);
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
ValidAudience = audienceId,
ValidIssuer = _issuer,
IssuerSigningKey = signingKey // Cannot convert HMAC Signing Credentials to System.IdentityModel.Tokens.SecurityKey
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true
};
var handler = new JwtSecurityTokenHandler();
SecurityToken token = null;
var principal = handler.ValidateToken(protectedText, tokenValidationParameters, out token);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}
One issue right off the jump is the issuer signing key. I am having trouble coming up with an acceptable parameter. I am seeing the error message:
Cannot convert HMAC Signing Credentials to
System.IdentityModel.Tokens.SecurityKey
To be honest, I am unsure why the Protect method needs to fire. I thought the flow would end with the returning of the JWT token, but apparently not. Now I am struggling with the implementation of the Unprotect method as it is something I have never had to struggle with previously.
Even if I set all of the options to 'false' on the tokenValidationParamters I am still getting the following error on validation:
An exception of type
'System.IdentityModel.SignatureVerificationFailedException' occurred
in System.IdentityModel.Tokens.Jwt.dll but was not handled in user
code
Additional information: IDX10503: Signature validation failed. Keys
tried: ''.
Exceptions caught:
''.
token:
'{"typ":"JWT","alg":"HS256"}.{"iss":"http://myissuer.com","aud":"346e886acabf457cb9f721f615ff996c","exp":1510925372,"nbf":1510925072}'
When I compare the values to the decrypted token using JWT.IO all of the values match as expected.
Any guidance on what I may be doing wrong here, or how to call validateToken with a valid signing key on the tokenvalidationparamters would be most helpful and appreciated.
Thanks in advance!
EDIT:
I am curious why the Unprotect is firing at all... I thought I should be returning a JWT token to the client. The method to return a JWT fires before the Unprotect method and the JWT is never returned to the client.
Do I have something configured incorrectly?
In your Startup.cs file call the following methods. The Protect method in your customJwtFormat is called when the user actually tries to sign in to the authentication server endpoint. The unprotect method is called when the user tries to access a protected api url via the "Bearer [token]" authentication model. If you don't say for example app.UseOAuthBearerAuthentication in your Startup.cs file you'll end up getting errors such as
Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware
Warning: 0 : invalid bearer token received
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AccessTokenFormat = _tokenFormat
});
Create your customjwtFormat like below, you can alter the implementation as required.
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
//var signingKey = new HmacSigningCredentials(keyByteArray);
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
securityKey.KeyId = ConfigurationManager.AppSettings["as:AudienceId"];
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string authority = ConfigurationManager.AppSettings["Authority"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
ValidAudience = audienceId,
ValidIssuer = _issuer,
IssuerSigningKey = signingKey,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true
};
var handler = new JwtSecurityTokenHandler();
SecurityToken token = null;
// Unpack token
var pt = handler.ReadJwtToken(protectedText);
string t = pt.RawData;
var principal = handler.ValidateToken(t, tokenValidationParameters, out token);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}
you should use something like this
public AuthenticationTicket Unprotect(string protectedText)
{
if (string.IsNullOrWhiteSpace(protectedText))
{
throw new ArgumentNullException("protectedText");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(protectedText);
SecurityToken validatedToken;
string t = ((JwtSecurityToken)token).RawData;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
//var signingKey = new HmacSigningCredentials(keyByteArray);
var validationParams = new TokenValidationParameters()
{
ValidateLifetime = false,
ValidAudience = audienceId,
ValidIssuer = audienceId,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningToken = new BinarySecretSecurityToken(keyByteArray)
};
var principal = handler.ValidateToken(t, validationParams, out validatedToken);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}

JWT generation and validation in .net throws "Key is not supported"

I'm generating and validating a JWT with the following code.
static string GenerateToken()
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.pfx", "123");
var rsa = certificate.GetRSAPrivateKey();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(),
Issuer = "Self",
IssuedAt = DateTime.Now,
Audience = "Others",
Expires = DateTime.MaxValue,
SigningCredentials = new SigningCredentials(
new RsaSecurityKey(rsa),
SecurityAlgorithms.RsaSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
static bool ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.cer");
var rsa = certificate.GetRSAPublicKey();
var validationParameters = new TokenValidationParameters
{
ValidAudience = "Others",
ValidIssuer = "Self",
IssuerSigningKey = new RsaSecurityKey(rsa)
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
if (principal == null)
return false;
if (securityToken == null)
return false;
return true;
}
I have this code in a library which targets .net standard 2.0 and net46.
When I use the library in an .net core app 2.0 project everything is working as expected. I use the following nuget packages.
System.IdentityModel.Tokens.Jwt => 5.1.4
System.Security.Cryptography.Csp => 4.3.0
But when I build the same code with .net46 I get the following exception when trying to generate a token.
var token = tokenHandler.CreateToken(tokenDescriptor);
System.NotSupportedException: 'NotSupported_Method'
The the following exception is throw when I try to validate a token.
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: 'IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId:
'.
Instead of using an RsaSecurityKey I directly use an X509SecurityKey now. This works for both netstandard2.0 and net46.
static string GenerateToken()
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.pfx", "123");
var securityKey = new X509SecurityKey(certificate);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(),
Issuer = "Self",
IssuedAt = DateTime.Now,
Audience = "Others",
Expires = DateTime.MaxValue,
SigningCredentials = new SigningCredentials(
securityKey,
SecurityAlgorithms.RsaSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
static bool ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.cer");
var securityKey = new X509SecurityKey(certificate);
var validationParameters = new TokenValidationParameters
{
ValidAudience = "Others",
ValidIssuer = "Self",
IssuerSigningKey = securityKey
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
if (principal == null)
return false;
if (securityToken == null)
return false;
return true;
}
Also I only need the System.IdentityModel.Tokens.Jwt nuget package and can remove the System.Security.Cryptography.Csp package.
Thank you NtFrex ..
I just put minor changes to NtFrex's answer to make it work for me. And this works with .net 4.5.1 as well & I thought it might help someone. Here is the final code but first create a certificate. I've used openssl to create one with RSA512.
Create Token :
private string GenerateToken1()
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"C:\Users\myname\my-cert.pfx", "mypassword");
var securityKey = new X509SecurityKey(certificate);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(),
Issuer = "Self",
IssuedAt = DateTime.Now,
Audience = "Others",
Expires = DateTime.Now.AddMinutes(30),
SigningCredentials = new SigningCredentials(
securityKey,
SecurityAlgorithms.RsaSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Validate Token :
private bool ValidateToken1(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"C:\Users\myname\my-cert.pfx", "mypassword");
var securityKey = new X509SecurityKey(certificate);
var validationParameters = new TokenValidationParameters
{
ValidAudience = "Others",
ValidIssuer = "Self",
IssuerSigningKey = securityKey
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
if (principal == null)
return false;
if (securityToken == null)
return false;
return true;
}

How to encrypt JWT security token?

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?
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);
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),
};
});
My understanding is that Microsoft's JWT implementation doesn't currently support encryption (only signing).
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;

Categories