Encrypt JWT token - c#

We are using the JWT Nuget to create and validate token. Here is the code that we use to create token
private string CreateAccessToken(Dictionary<string, object> payload)
{
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, GetJWTKey());
return token;
}
My understanding is, this doesn't encrypt the token as I was able to parse the token by visting jwt.io and was able to read the contents. I would like to encrypt the token such that it should not be parsed. I wasn't able to find any method in JWT Nuget through which I can encrypt the token.
So how do I sign and encrypt the token using JWT Nuget?
Edit:
I understand that JWT doesn't require any encryption as only the authenticated user will be able to read the token which means, I am reading about my own contents and also, the actual communication will be over secured layer. So actually there is no need to encrypt the token yet, my requirement is the token shouldn't be human readable

Your understanding is correct but you are missing an important feature of JWT: encrypting the token is not a purpose of JWT.
The secret used by the algorithm is used to sign the token and not for encrypting it, for more information take a look to the RFC 7515.
As suggested in the comments below there is also the RFC 7516, Json Web Encryption (JWE).
For using JWE inside a C# application you have to use the System.IdentityModel.Tokens.Jwt package, and then something like:
var handler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Audience = "audience",
//other property
EncryptingCredentials = new X509EncryptingCredentials(new X509Certificate2("path/to/public/key"))
};

Related

How to validate the JWT token of keycloak in C# .Net?

Once we get the JWT token in the frontent, we can pass is using Authorization header or through cookies for authenticating our stateless RestAPIs in the backend server. Nicely explained in this video.
If the backend server is in C# .Net Framework (MVC), how can the received JWT be validated? The official documentation points towards OWIN, which is not maintained.
When seeing the various blogs and documentation, the theory says we need to get the Modulus & Exponent parameters from the Keycloak Realms' public certificate, and then verify it using JWT.Net
How can that be achieved?
Get the public certificate of your realm through keycloak:
You will get something like this that needs to be formatted properly:
Copy the PublicCertificate of your realm
Save it in a KeyCloakRealm.Public.crt file
Add header -----BEGIN CERTIFICATE-----
Make the single line certificate to be in 64bytes in each line
Add footer -----END CERTIFICATE-----
Then the following code snippet might be used to validate the received JWT token.
X509Certificate2 certificate = new
X509Certificate2("KeyCloakRealm.Public.crt");
RSACryptoServiceProvider key =(RSACryptoServiceProvider)certificate.PublicKey.Key;
RSAParameters rsaParameters = key.ExportParameters(false);
RSA rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
var json = JwtBuilder.Create()
.WithAlgorithm(new RS256Algorithm(rsa)) // asymmetric
.MustVerifySignature()
.Decode(token);
// The above method will throw an appropriate error if the JWT is invalid or cannot be validated against the supplied public keycloak realm
// If there is no exception, you will get the data in your json object

JWT Invalid Signatures in .NET libraries

I am currently struggling with creating signatures with any .net library to access Twitch api endpoints. The jwt.io page always says the signature is invalid when I copy paste my generated jwt there. When I edit the secret the signature is obviously adjusted and when I use the adjusted jwt I can make the Twitch api call without a problem. When I use my generated jwt I always end up with a 401 authentication failed.
I tried pretty much all of the .net libraries now and all of them generate a wrong signature for HS256… I am not sure what I do wrong since I am new to JWT and couldn’t find anything that points me to the right direction. Here is my code to generate the jwt using the jwt.net library (https://github.com/jwt-dotnet/jwt). Would highly appreciate if someone could help me with this.
var payload = new Dictionary<string, object>
{
{ "exp", DateTimeOffset.Now.AddSeconds(360).ToUnixTimeSeconds()},
{ "user_id", "668312333"},
{ "role", "external"},
};
var secret = Encoding.UTF8.GetString(Convert.FromBase64String("MY SHARED EXTENSION SECRET FROM TWITCH"));
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
you need to set the secret to be a byte array as that is what is needed by the underlying library :-
var secret = Convert.FromBase64String("MY SHARED EXTENSION SECRET FROM TWITCH");

Is there a ready to use "GenerateJwt" method?

I'm using the Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt for my .NET Core project. In my Startup file I run the configuration setup for the [Authorize] annotation. This works fine for me when I'm generating new tokens with my own method (sample)
public object GenerateToken(Dictionary<string, object> payload)
{
DateTime tokenExpiresAt = DateTime.Now.AddMilliseconds(1); // From config
byte[] symmetricKey = Convert.FromBase64String("secret"); // from config
SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(symmetricKey);
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
{
Claims = payload,
Expires = tokenExpiresAt,
SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
string token = tokenHandler.WriteToken(securityToken);
return new { token, tokenExpiresAt };
}
The validation of a token doesn't need to be implemented because it's done with the [Authorize] annotation. I would like to know if there is a method I can use to generate a token and don't have to code it on my own? I'm storing the generated tokens to a database and also need to return the expiration time.
So yes, the solution above works fine for me but maybe it's redundant :)
Is there a method that takes the token secret, the payload and the time the token will expire? E.g. TokenGenerator.Sign("secret", payload, tokenExpiresAt)?
Microsoft libraries don't support issuing tokens natively, so there's no one command in a Microsoft library like you're looking for. However Microsoft does issue tokens as an identity server using their service azure ad, that would probably be their easiest way.
The way you're doing is basically fine if you're just doing that. and not full authentication framework, here's an example of people doing very similar thing to you: https://jasonwatmore.com/post/2019/10/11/aspnet-core-3-jwt-authentication-tutorial-with-example-api
If you are looking to implement your own complete authentication service that can issue tokens. there are some relatively common 3rd party libraries that will help you not have to reinvent the wheel, one of which is identityserver4:
https://identityserver4.readthedocs.io/en/latest/index.html
it's a full identity provider solution.
another one is openiddict https://devblogs.microsoft.com/aspnet/bearer-token-authentication-in-asp-net-core/

IdentityServer Tutorial , token has Invalid Signature

I created a test application with the identity server.
It is very simple. it has some hard coded InMemory Users,Clients and SCopes and uses the idsrv3test.pfx certificated from the samples for signing
var factory = new IdentityServerServiceFactory();
factory
.UseInMemoryUsers(MemoryUsers.All())
.UseInMemoryClients(MemoryUsers.GetClients())
.UseInMemoryScopes(MemoryUsers.GetScopes());
var cert = new X509Certificate2(#"..\certs\idsrv3test.pfx", "idsrv3test");
var options = new IdentityServerOptions()
{
Factory = factory,
EnableWelcomePage = true,
SigningCertificate = cert,
RequireSsl = false
};
app.UseIdentityServer(options);
Now I get a a token via the connect/token endpoint. as grant type I use password.
This succeeds and I got a bearer token back.
now I wanted to validated the token contents on jwt.io . I shows me all the informations of all parts of the token. but at the end of the site it shows me "invalid signature"
Is this the result of a bug ? Or just a result that I use this test certificate?
Jwt.io cannot validate RS256 signatures. Only HS256.

Dealing with long bearer tokens from webapi by providing a surrogate token

I am building a web api using ASP.NET WebApi 2 using claims authentication, and my users can have very large number of claims. With a large number of claims the bearer token grows very large quickly, so I am attempting to find a way of returning a much shorter bearer token.
SO far I have discovered that I can provide a IAuthenticationTokenProvider to the OAuth options OAuthAuthorizationServerOptions.AccessTokenProvider property:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(12),
AccessTokenProvider = new GuidProvider() // <-- here
};
And this gives me a chance to intercept the AuthenticationTicket and stash it away, replacing it with something simpler - in my example below a hashed guid. (Note: At the moment this class simply holds a ConcurrentDictionary<string,AuthenticationTicket> with my sessions - in a real-world example I intend to store the sessions in some persistent storage)
public class GuidProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> tokens
= new ConcurrentDictionary<string, AuthenticationTicket>();
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
var ticket = Crypto.Hash(guid);
tokens.TryAdd(ticket, context.Ticket);
context.SetToken(ticket);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (tokens.TryGetValue(context.Token, out ticket))
{
if (ticket.Properties.ExpiresUtc.Value < DateTime.UtcNow)
{
tokens.TryRemove(context.Token, out ticket);
}
context.SetTicket(ticket);
}
}
}
So my questions:
Is this an appropriate (and secure!) way of providing a surrogate key in place of my long claims-generated token?
Is there perhaps a better/easier place where I should be doing this within the webapi/OAuth stack?
Another thing to note is that I intend to support refresh tokens, and in fact the example above was pulled from examples which use this sort of mechanism for the Refresh token - except with a refresh token they appear to be single-use, so the ReceiveAsync method would usually always remove the refresh token supplied from the ConcurrentDictionary, I'm not entirely sure I understand why?
I do not recommend to do this because you are eventually going to store the authentication tickets into the database or Redis server, the draw back here that with each request containing a bearer token, you are going to check this permanent store in order to resolve the Guid and get the ticket again to construct it.
I suggest that you use JSON Web Token JWT instead of the default bearer access tokens format, to do this you need implement your custom access token format CustomOAuthProvider in property Provider in OAuthAuthorizationServerOptions as the code below:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://jwtauthzsrv.azurewebsites.net")
};
I've noticed that adding more claims to the JWT token won't increase its size dramatically as the case of default access token format.
Below a sample of 2 JWTs with different claims inside each one, the second one is larger than the first by only 50 chars. I recommend you to check the encoded content of each one using jwt.io
First JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzMyNywibmJmIjoxNDE4NjQ1NTI3fQ.vH9XPtjtAv2-6SwlyX4fKNJfm5ZTVHd_9a3bRgkA_LI
Second JWT (More claims):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciIsIlN1cGVydmlzb3IxIiwiU3VwZXJ2aXNvcjIiLCJTdXBlcnZpc29yMyJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzQ1NiwibmJmIjoxNDE4NjQ1NjU2fQ.TFEGDtz1RN8VmCQu7JH4Iug0B8UlWDLVrIlvc-7IK3E
The JWT format is becoming the standard way to issue OAuth 2.0 bearer tokens, as well it will work with refresh token grant. But keep in mind that JWT is only signed tokens and not encrypted as the case in default access token format, so do not store confidential data in.
I've written detailed blog post on bitoftech.net on how to use JWT tokens in ASP.NET Web API along with a live demo API and source code on GIthub, feel free to check it and let me know if you need more help.
Good luck!

Categories

Resources