C# Algorithm / Class to generate a secure password with expiry - c#

Since I do not want to reinvent the wheel I'm pretty sure there is a solution already out there: I want to generate a password (based on a different one, byte[] or whatever) that has an expiry. The idea is that a client-generated password is only valid for a specific time on the server. So as long as their clocks aren't too different they can communicate with alternating password. Sounds pretty much like certificates so there sure already is a solution out there.

As it seems to me you are looking for a token approach. You can check JWT it has expiry date and can be easily validated at server side.
Here is a small example:
string GenerateToken()
{
var mySecret = "mySecretmySecretmySecret";
var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
NotBefore = DateTime.UtcNow.AddSeconds(-2),
Expires = DateTime.UtcNow.AddSeconds(2),
SigningCredentials = new SigningCredentials(mySecurityKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
bool ValidateCurrentToken(string token)
{
var mySecret = "mySecretmySecretmySecret";
var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = mySecurityKey
}, out SecurityToken validatedToken);
}
catch
{
return false;
}
return true;
}
Notice the ClockSkew = TimeSpan.Zero here. By default MS allows the tokens to differ up to 5 minutes. We only want to use the absolute token's expiry.

Related

C#: JWT bearer token authentication failure: Unauthorized

I'm encrypting JWT with a cert on the client-side and trying to validate that JWT on the server-side but it is always failing. Can anyone please help?
Sender
var handler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor {
Audience = "https://you.com",
Issuer = "https://me.com",
Subject = new ClaimsIdentity(new List < Claim > { new Claim("sub", "MyName")),
IssuedAt = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddMinutes(50),
EncryptingCredentials = new X509EncryptingCredentials(new X509Certificate2("certificate.crt", "XYZ"))
};
var JWT = handler.CreateEncodedJwt(tokenDescriptor);
Tested the above JWT token to check if the cert and pfx file are correct.
var handler = new JwtSecurityTokenHandler();
var claimsPrincipal = handler.ValidateToken(
token,
new TokenValidationParameters
{
ValidAudience = "https://you.com",
ValidIssuer = "https://me.com",
RequireSignedTokens = false,
TokenDecryptionKey = new X509SecurityKey(new X509Certificate2("certificate.pfx", "XYZ"))
},
out SecurityToken securityToken);
securityToken has all the info passed in the JWT token. So I know that there is no issue with cert or pfx file
Passing the JWT token ("Bearer {token}") in my rest call as header
Receiver
[HttpPut("Authenticate"), Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<IActionResult> Auth([FromBody] User user)
{
// stuff
}
public void ConfigureServices(IServiceCollection services) {
//...
X509Certificate2 cert = new X509Certificate2("certificate.pfx", "XYZ");
SecurityKey key = new X509SecurityKey(cert);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => {
options.Authority = "https://me.com";
options.Audience = "https://you.com";
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://me.com",
ValidAudience = "https://you.com",
IssuerSigningKey = key
};
});
}
In the rest call, I'm always getting 401: Unauthorized error. The cert and pfx files seem fine. Am I doing something wrong on the client-side or do I need to do anything else on client side to authorize the JWT ?

Swapping SymmetricSecurityKey for AsymmetricSecurityKey for JWT

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.
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.

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

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