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

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

Related

JwtSecurityTokenHandler NotSupportedException at EncryptValue

I have a JsonWebTokenFormat class which creates a JWT token and signs it with a X.509 RSA SSH 256 certificate.
internal class JsonWebTokenFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer;
private readonly ICertificateStore _store;
public JsonWebTokenFormat(string issuer, ICertificateStore store)
{
_issuer = issuer;
_store = store;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
RSA rsaPrivateKey = _store.GetCurrentUserPrivateCertificate(_issuer);
SigningCredentials signingCredentials = new SigningCredentials(new RsaSecurityKey(rsaPrivateKey), SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
DateTimeOffset? issued = data.Properties.IssuedUtc;
DateTimeOffset? expires = data.Properties.ExpiresUtc;
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
issuer: _issuer,
claims: data.Identity.Claims,
notBefore: issued.Value.UtcDateTime,
expires: expires.Value.UtcDateTime,
signingCredentials: signingCredentials);
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
string jwtAuthToken = jwtSecurityTokenHandler.WriteToken(jwtSecurityToken);
return jwtAuthToken;
}
public AuthenticationTicket Unprotect(string jwtToken)
{
// read the issuer from the token
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(jwtToken);
RSA rsaPublicKey = _store.GetPublicCertificateForClient(jwtSecurityToken.Issuer);
TokenValidationParameters tokenValidationParams = new TokenValidationParameters
{
ValidIssuer = _issuer,
RequireExpirationTime = true,
ValidateIssuer = true,
RequireSignedTokens = true,
ValidateLifetime = true,
ValidateAudience = false,
IssuerSigningKey = new RsaSecurityKey(rsaPublicKey),
ValidateIssuerSigningKey = true
};
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
SecurityToken tempToken;
ClaimsPrincipal principal = jwtSecurityTokenHandler.ValidateToken(jwtToken, tokenValidationParams, out tempToken);
AuthenticationTicket authenticationTicket = new AuthenticationTicket(new ClaimsIdentity(principal.Identity), new AuthenticationProperties());
return authenticationTicket;
}
}
And the ICertificateStore implementation looks like this:
class MockCertificateStore : ICertificateStore
{
private readonly X509Certificate2 _certificate;
public MockCertificateStore()
{
_certificate = new X509Certificate2(
#"C:\certs\test-client.pfx",
"12345",
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.Exportable);
}
public RSA GetCurrentUserPrivateCertificate(string subject)
{
return _certificate.GetRSAPrivateKey();
}
public RSA GetPublicCertificateForClient(string clientId)
{
return _certificate.GetRSAPublicKey();
}
}
So I have this unit test that tests this class and it works fine on my local machine (and other developers' local machines) but it fails on our Jenkins build environment.
It fails with the following exception:
Test method AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken threw exception:
System.NotSupportedException: Method is not supported.
Stack Trace:
at System.Security.Cryptography.RSA.DecryptValue(Byte[] rgb)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at System.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Byte[] input) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\AsymmetricSignatureProvider.cs:line 224
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.CreateSignature(String inputString, SecurityKey key, String algorithm, SignatureProvider signatureProvider) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 854
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.WriteToken(SecurityToken token) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 815
at AuthCore.Token.JsonWebTokenFormat.Protect(AuthenticationTicket data) in C:\Jenkins\workspace\AuthCore\Token\JsonWebTokenFormat.cs:line 38
at AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken() in C:\Jenkins\workspace\AuthCore.Tests\Token\JsonWebTokenFormatTests.cs:line 34
Any help is appreciated. I've looked at a bunch of SO questions and none of them helped.
Solved!
The problem was that the RsaSecurityKey class has been deprecated since .NET 4.6.0. For some reason, this class is throwing when used in a computer that doesn't have older version of .NET installed, but it was fine on computer with older versions of .NET. Instead, just use X509SecurityKey class.
Refer to the following article for potential solution:
https://q-a-assistant.com/computer-internet-technology/338482_jwt-generation-and-validation-in-net-throws-key-is-not-supported.html
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;
}

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

dnx451 RC1 What happened to InMemorySymmetricSecurityKey?

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

OAuth Bearer token Authentication is not passing signature validation

I get the following error on the token consumer. Any help resolving this will be most appreciated. Thanks.
"IDX10503: Signature validation failed.
Keys tried:
'System.IdentityModel.Tokens.SymmetricSecurityKey '. Exceptions
caught: 'System.InvalidOperationException: IDX10636:
SignatureProviderFactory.CreateForVerifying returned null for key:
'System.IdentityModel.Tokens.SymmetricSecurityKey',
signatureAlgorithm:
'http://www.w3.org/2001/04/xmldsig-more#hmac-sha256'. at
Microsoft.IdentityModel.Logging.LogHelper.Throw(String message, Type
exceptionType, EventLevel logLevel, Exception innerException) at
System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(Byte[]
encodedBytes, Byte[] signature, SecurityKey key, String algorithm) at
System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String
token, TokenValidationParameters validationParameters) '. token:
'token info was here'"
Token Generation Code on OAuth server
using (var ctlr = new EntityController())
{
var authRepo = ctlr.GetAuthModelRepository();
string clientId;
ticket.Properties.Dictionary.TryGetValue(WebConstants.OwinContextProps.OAuthClientIdPropertyKey, out clientId);
if (string.IsNullOrWhiteSpace(clientId))
{
throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
}
//audience record
var client = authRepo.FindAuthClientByOAuthClientID(clientId);
var issued = ticket.Properties.IssuedUtc;
var expires = ticket.Properties.ExpiresUtc;
var hmac = new HMACSHA256(Convert.FromBase64String(client.Secret));
var signingCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(hmac.Key),
Algorithms.HmacSha256Signature, Algorithms.Sha256Digest);
TokenValidationParameters validationParams =
new TokenValidationParameters()
{
ValidAudience = clientId,
ValidIssuer = _issuer,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true,
IssuerSigningToken = new BinarySecretSecurityToken(hmac.Key)
};
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = new JwtSecurityToken(_issuer, clientId, ticket.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials);
jwtOnTheWire = jwtHandler.WriteToken(jwt);
SecurityToken validatedToken = null;
jwtHandler.ValidateToken(jwtOnTheWire, validationParams,out validatedToken);
if (validatedToken == null)
return "token_validation_failed";
}
return jwtOnTheWire;
Token Consumption\validation ASP.Net 5 vNext site within Owin Startup.cs
public void ConfigureServices(IServiceCollection services)
services.ConfigureOAuthBearerAuthentication(config =>
{
//oauth validation
var clientSecret = "not the real secret";
var hmac = new HMACSHA256(Convert.FromBase64String(clientSecret));
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(hmac.Key), Algorithms.HmacSha256Signature, Algorithms.Sha256Digest);
config.TokenValidationParameters.ValidAudience = "myappname";
config.TokenValidationParameters.ValidIssuer = "mydomain.com";
config.TokenValidationParameters.RequireSignedTokens = true;
config.TokenValidationParameters.RequireExpirationTime = true;
config.TokenValidationParameters.ValidateLifetime = true;
config.TokenValidationParameters.ValidateIssuerSigningKey = true;
config.TokenValidationParameters.ValidateSignature = true;
config.TokenValidationParameters.ValidateAudience = true;
config.TokenValidationParameters.IssuerSigningKey = signingCredentials.SigningKey;
});
public void Configure(IApplicationBuilder app)
app.UseOAuthBearerAuthentication(config =>
{
config.AuthenticationScheme = "Bearer";
config.AutomaticAuthentication = true;
});
I was able to add my own signature validation to the TokenValidationParameters Then I compared the incoming Raw signature of the JWT to the compiled signature in this code and if it matches the signature is valid.
Why this didn't happen using the builtin signature validation is beyond me, maybe it's a possible bug in beta 6 of the vNext Identity token framework.
public void ConfigureServices(IServiceCollection services)
config.TokenValidationParameters.SignatureValidator =
delegate (string token, TokenValidationParameters parameters)
{
var clientSecret = "not the real secret";
var jwt = new JwtSecurityToken(token);
var hmac = new HMACSHA256(Convert.FromBase64String(clientSecret));
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(hmac.Key), SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest);
var signKey = signingCredentials.SigningKey as SymmetricSecurityKey;
var encodedData = jwt.EncodedHeader + "." + jwt.EncodedPayload;
var compiledSignature = Encode(encodedData, signKey.Key);
//Validate the incoming jwt signature against the header and payload of the token
if (compiledSignature != jwt.RawSignature)
{
throw new Exception("Token signature validation failed.");
}
return jwt;
};
Encode helper method
public string Encode(string input, byte[] key)
{
HMACSHA256 myhmacsha = new HMACSHA256(key);
byte[] byteArray = Encoding.UTF8.GetBytes(input);
MemoryStream stream = new MemoryStream(byteArray);
byte[] hashValue = myhmacsha.ComputeHash(stream);
return Base64UrlEncoder.Encode(hashValue);
}

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