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 ?
Related
I have a website where users log in on an external website and are redirected back with a token in the querystring. I then validate the token and create the authentication ticket. How would I check on subsequent requests that the user is now logged in? And how would I log a user out?
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Request.Query["token"].FirstOrDefault();
if (token != null)
{
var validator = new JwtSecurityTokenHandler();
var region = "us-xxx-x";
var userPoolId = "us-xxx-xxxxxxx";
var appClientId = "xxxxxxxxxxxxxxxxxxxx";
var cognitoIssuer = $"https://cognito-idp.{region}.amazonaws.com/{userPoolId}";
var jwtKeySetUrl = $"{cognitoIssuer}/.well-known/jwks.json";
var validationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(jwtKeySetUrl);
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
return (IEnumerable<SecurityKey>)keys;
},
ValidIssuer = cognitoIssuer,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = appClientId
};
// validate the token
var principal = validator.ValidateToken(token, validationParameters, out var validatedToken);
if (principal.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
{
var claims = new[] { new Claim("token", token) };
var identity = new ClaimsIdentity(claims, nameof(TokenAuthenticationHandler));
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), this.Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
else
return Task.FromResult(AuthenticateResult.Fail("Token validation failed"));
}
else
return Task.FromResult(AuthenticateResult.Fail("No token"));
}
I tried with the code:
In controller:
[Authorize(AuthenticationSchemes = "Cookies,Bearer")]
public IActionResult Index()
{
return View();
}
public IActionResult CreateToken()
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, "Jeffcky"),
};
var token = GenerateToken(claims);
Response.Cookies.Append("x-access-token", token);
return RedirectToAction("Login");
}
private string GenerateToken(Claim[] claims)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456"));
var token = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5001",
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddMinutes(5),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
[HttpPost]
public async Task<IActionResult> Login(User user)
{
var token = Request.Cookies["x-access-token"];
var jsonToken = new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
var username = jsonToken.Claims.FirstOrDefault(m => m.Type == ClaimTypes.Name).Value;
if (username!=user.Name)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, "Jeffcky"),
};
var claimsIdentity = new ClaimsIdentity(claims, "Login");
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
}
return RedirectToAction("Index");
}
In startup:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie( m =>
{
m.LoginPath = new Microsoft.AspNetCore.Http.PathString("/Home/Login");
m.AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("....");
m.SlidingExpiration = true;
m.ExpireTimeSpan = TimeSpan.FromMinutes(120);
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")),
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000",
ValidateAudience = true,
ValidAudience = "http://localhost:5001",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Cookies["x-access-token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
The Result:
you could modify the codes in login controller to pool data from database and varify the userinformation to meet your requirement.
And the Attribute[Authorize(AuthenticationSchemes = "Cookies,Bearer")] allows you Authentication with cookie or jwt,it may help in some situation
and if you want to logout,you could delete the cookie which strores the token or ticket
UpDate:
Accroding to your description,I think you could use jwt Authentication and set in your startup as follow:
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["tookn"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
Is it the result you excepted?
Full disclosure, I'm not very knowledgeable on the library you are using or on what exactly you are trying to do here, but I can tell you how these things work, generally speaking. Conceptually logging in and out on a web service works as follows:
Client sends authentication information to the server (Username, password hash, 2FA info, etc.)
The server checks the authentication information against the stored information about the user (e.g. compare password hash to the stored password hash)
If authentication is sucessful, the server creates and stores a cookie (a long randomly generated string). The server stores the cookie along with information such as the user who the cookie belongs to, and when the cookie was created.
The server sends the cookie to the client. The client is now "logged in".
Now when the client wants to send some http request to the web service, they send the cookie along with the request. The server then looks up who the cookie belongs to and handles the http request appropriately.
When the user wants to log out they send an http request to the server asking to log out, and pass the cookie along with the message. The server then invalidates the cookie, and further http requests are handled as if the user is logged out.
I have started a simple web api and added token generation to it using jwt. However I used in app accounts for the user store this is my function for setting up the token. Its showing up in swagger fine but what I dont get is how to I enforce the username and password to be entered when the request for token is made. Or is that bad practise.
This is my class when I generate the security token.
public JwtService(IConfiguration config)
{
var test = config;
_secret = config.GetSection("JwtToken").GetSection("SecretKey").Value;
_expDate = config.GetSection("JwtToken").GetSection("expirationInMinutes").Value;
}
public string GenerateSecurityToken(string email)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, email)
})
,
Expires = DateTime.UtcNow.AddMinutes(double.Parse(_expDate)),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
public static IServiceCollection AddTokenAuthentication(this IServiceCollection services, IConfiguration config)
{
var secret = config.GetSection("JwtToken").GetSection("SecretKey").Value;
var keySecret = Base64UrlEncoder.DecodeBytes(secret);
var key = Encoding.ASCII.GetBytes(keySecret.ToString());
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
// ValidIssuer = "localhost",
//ValidAudience = "localhost"
};
});
return services;
}
Swagger Gen Code
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "App Manager - Running Buddies", Version = "v1" });
c.AddSecurityDefinition("bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme.",
});
});
Swagger Ui Test
You might want to pass username/password in a model using HTTP POST.
Be sure to only issue a token when the login request is valid, i.e. after you have successfully authenticated the user.
See Securing ASP.NET Core 2.0 Applications with JWTs
for further details.
Edit: To perform a login using Identity you could use SignInManager.PasswordSignInAsync or to just check the credentials SignInManager.CheckPasswordSignInAsync. See the samples for an example:
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
// generate the token
}
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)
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());
}
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;