JWT TOKEN Generate Client_Assertion - c#

I am newbie in JWT access Token generation. I have Public Key, Private key and ClientID. I need to generate Client_Assertion.
client_assertion: JWT (signed by client ID, public certificate and private key using
RS256 as the signature algorithm).
I have found some Node.JS code but I want to do it using .Net Framework (Not .Net Core)
Node.Js code can be seen on this link
I need to do it in C#. From where to start and how to achieve it?

I used the .NET libraries
System.IdentityModel.Tokens.Jwt
Microsoft.IdentityModel.Tokens
Microsoft.IdentityModel.JsonWebTokens
I'm also assuming that, as you said you have a private key, and that you've loaded that into an RSACryptoServiceProvider
Here's my sample code
First create your claims. This is your JWT payload
var claims = new Claim[]
{
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Sub, "your subject"),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Iat, DateTime.Now.ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
new Claim(MicrosoftJwt.JwtRegisteredClaimNames.Exp, DateTime.Now.AddMinutes(60).ToEpochSeconds().ToString(), ClaimValueTypes.Integer),
};
Then you need to configure your signing credentials using your private key.
// Assuming you already have your key loaded into an RSACryptoServiceProvider
var key = new MicrosoftTokens.RsaSecurityKey(csp)
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
Now you can create your token
var jwt = new JwtSecurityToken(
issuer : issuer,
audience : audience,
claims : claims,
signingCredentials: signingCredentials
);
// You can add extra items to your Header if necessary
jwt.Header.Add("kid", deviceId);
You can then write your token to a string
var assertion = new JwtSecurityTokenHandler().WriteToken(jwt);
I think the value of assertion is what you're trying to get

Related

Is there a way in c# where I can sign a Jwt with a key I created on linux using openssl genrsa -out server.key 2048

created the private key with the following command
openssl genrsa -out server.key 2048
In c# console app tried the following:
string keyFile = Path to keyFile
var secret = File.ReadAllText(keyFile);
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.RsaSsaPssSha256);
var token = new JwtSecurityToken(
claims: claims, expires:DateTime.Now.AddDays(1), signingCredentials: creds);
token.Header.Add("kid", "7fab807d-4988-4012-8f10-a77655787450");
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
The WriteToken throws the following error message
System.NotSupportedException
HResult=0x80131515
Message=IDX10634: Unable to create the SignatureProvider.
Algorithm: 'PS256', SecurityKey: '[PII of type 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'
is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
Source=Microsoft.IdentityModel.Tokens
StackTrace:
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm, Boolean cacheProvider)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm)
at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
at JwtConsole.Program.CreateSignedJwtToken() in C:\Projects\Jwt\dotnetcore-jwt-manager\JwtConsole\Program.cs:line 244
at JwtConsole.Program.Main(String[] args) in C:\Projects\Jwt\dotnetcore-jwt-manager\JwtConsole\Program.cs:line 34
Please note very in-experienced developer any help greatly appreciated
Thank you
The exception shows you a list of supported algorithms, see link. The chosen algorithm you used is not supported on non-Windows systems.
You should use a different algorithm, preferably one of the ECDSA algorithms.
It is the line with SigningCredentials - replace the SecurityAlgorithms.RsaSsaPssSha256 by a supported algorithm as per link - selected algorithm must be supported in both .NET Framework and .NET Standard 2.0 frameworks. The modified version of your code is below.
string keyFile = Path to keyFile
var secret = File.ReadAllText(keyFile);
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);
var token = new JwtSecurityToken(claims: claims, expires: DateTime.Now.AddDays(1), signingCredentials: creds);
token.Header.Add("kid", "7fab807d-4988-4012-8f10-a77655787450");
var jwt = new JwtSecurityTokenHandler().WriteToken(token);

Differences between AspNetUserToken and custom made JWT token in .NET Core 3.0

I'm working on a small side-project API and I wanted to implement JWT authentication. I already made some API with JWT and always made custom implementation and validation.
This time, I wanted to use Identity/Entity Framework to go faster and to use the tools that are already made for me.
While doing the GenerateToken method and looking through the internet, I noticed that a lot of the tables created by IdentityFramework are not used. And I got interested in AspNetUserToken.
I noticed that instead of
private object GenerateToken(IdentityUser user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(ApiConfig.JwtSecretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Email, user.Email),
}),
Expires = DateTime.UtcNow.AddSeconds(double.Parse(ApiConfig.JwtExp)), //TODO: Try parse
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Audience = ApiConfig.JwtAudience,
Issuer = ApiConfig.JwtIssuer
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Which I used to generate a new JWT token, add the claims, issuer, audience, etc... Could maybe be replaced by this :
//Removes existing token
_userManager.RemoveAuthenticationTokenAsync(user, "lboard", "login");
//Creates a new one
var newToken = await _userManager.GenerateUserTokenAsync(user, "lboard", "login");
//Set the new token for the user
await _userManager.SetAuthenticationTokenAsync(user, "lboard", "login", newToken);
I would like to know what are the differences between the two methods, and if there are any benefits of using a custom implementation or if I'm better off with the IdentityFramework one.
The GenerateUserTokenAsync methods is used internally by other UserManager methods like GenerateChangeEmailTokenAsync, GenerateChangePhoneNumberTokenAsync and so on. REF
In order to use more abstract GenerateUserTokenAsync, you must provide a token provider that actually generates the token. Since you don't have any default token providers for a JWT access token, you would still have to write the logic yourself and register your custom token provider and then you could use the GenerateUserTokenAsync method.
You would still need to write the JWT logic by yourself, incude claims etc, but with an added overhead.

get signature from TokenValidatedContext

I'm using the Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt packages for my .NET Core project.
When configuring the services I'm adding logic to the OnTokenValidated event.
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtBearerOptions =>
{
// ... set TokenValidationParameters ...
jwtBearerOptions.Events = new JwtBearerEvents()
{
OnTokenValidated = (tokenValidatedContext) =>
{
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
string tokenWithoutSignature = jwtSecurityTokenHandler.WriteToken(tokenValidatedContext.SecurityToken);
// ... full token from request? ...
}
};
});
Since I know the context only returns me the token without the signature I would like to know how I can
either get the full token with signature
or the signature additionally to add it to the tokenWithoutSignature string
If this is not possible:
I'm generating new tokens this way
public string GenerateAccessToken(IDictionary<string, object> payload)
{
SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Convert.FromBase64String("secret from config"));
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
{
Claims = payload,
Expires = DateTime.Now, // value from config
SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
string token = tokenHandler.WriteToken(securityToken);
return token;
}
Maybe I can either retrieve
the token string without the signature
only the signature
within this method?
If nothing works:
Since I kow a bearer token always contains three parts like
header.payload.signature
I could split the string segments to an array, take the first and second element from the array and create a new string of
firstString + . + secondString
That should give me the token without the signature. Are there any better ideas to cut off the signature from a full token?
Why do I want to achieve this?
This question is based on this one
Security token from TokenValidatedContext from the OnTokenValidated event listener is missing last string segment
I'm working with access and refresh tokens. During validation, I have to compare the token from the request with the token from the database. The token in the database contains the signature too. So I'm facing the same problem as linked above.
That's why I thought about multiple solutions and wrote them down here. If the TokenValidatedContext is not able to return me the signature it seems I have to store the JWT to the database without the signature. And also for this case, I need to separate the signature from the generated JWT.
Without using refresh tokens I only store the maximum session lifetime of a user to the database. The flow is based on this idea
Only store the time of the JWT with the highest lifetime to the database instead of the whole JWT
With using refresh tokens I came up with the following flow. Since you know that the OnTokenValidated handles the validation logic the following logic is additional. I have a database table with
username | access_token | refresh_token | refresh_token_expires_at
and the primary key is a composite key of username + access_token. Refresh tokens are just some random strings generated like so
public string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
and that's why I'm storing an additional expiration date to it. It should be able to expire after some time.
Signing in
Store the generated access and refresh token and its expiration time for a user to the database. Either store the full access token or the access token without signature to the database (depends on the solution of this question).
Hitting a protected endpoint
Check if that access token for that user exists in the database.
Hitting the refresh endpoint
Check if the database refresh token has expired. If not, compare this one with the refresh token from the request. If everything is fine, remove the old access and refresh token from the database and store the new generated access and refresh token to the database.
Signing out
Remove that access and its connected refresh token from the database.
I don't quite understand why you do all this, but if all you need is the original token, you can use one of these:
o.Events = new JwtBearerEvents
{
OnTokenValidated = (context) =>
{
var securityToken = (System.IdentityModel.Tokens.Jwt.JwtSecurityToken)context.SecurityToken;
var token = securityToken.RawData; // "ey...."
var tokenHeader = securityToken.RawHeader; // "ey...."
var tokenPayload = securityToken.RawPayload; // "ey...."
var tokenSignatur = securityToken.RawSignature; // "ey...."
var fullBearerHeader = context.Request.Headers["Authorization"]; // "Bearer ey..."
return Task.CompletedTask;
}
};
You probably want to make the code a bit more safe with regards to type casting etc., but it should give you the token.
Why do you want to manipulate the token? If it is just to Validate the token you can use below code.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
//ValidIssuer = Configuration["Issuer"],
//ValidAudience = Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Key"]))
};
});
and
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseMvc();
}

How to sign/encrypt JWT in C# with PEM key?

I need to create custom tokens that need to be signed using a key provided by Google. The key is provided as text, like -----BEGIN PRIVATE KEY-----\nMIIE....
I had this working by using BouncyCastle to read the PEM key and get the RSA keys, but now I need this project to run under Linux so I can't use BouncyCastle as it only works under Windows (it makes uses of RSACryptoServiceProvider).
So, is there a way I can achieve the same result but using only .NET Standard code?
I already tried to convert the PEM file to a PFX file, but I don't have any certificates. Tried to get the certificates from here or here didn't work (OpenSSL says that the certificates don't belong to the key provided).
You can convert your PEM file to p12 file and signed your JWT with that p12
var payload = new Dictionary<string, object>()
{
{ "sub", "mr.x#contoso.com" },
{ "exp", 1300819380 }
};
var privateKey=new X509Certificate2("my-key.p12", "password").GetRSAPrivateKey();
string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);
Here is the details link https://github.com/dvsekhvalnov/jose-jwt#rs--and-ps--family
The PemUtils library will do exactly what you need, it decodes the PEM and DER data without the use of RSACryptoServiceProvider. It's available as a NuGet package and in case you have any additional needs, I'm the author of that library, so feel free to open an issue on github.
Something like this should work:
using (var stream = File.OpenRead(path))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
var key = new RsaSecurityKey(RSA.Create(rsaParameters));
var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256);
}
Just to expand on the solution, this is the full code, working (this is for firebase auth tokens):
var privateKey = new X509Certificate2("cert.p12", "password");
var credentials = new SigningCredentials(new X509SecurityKey(privateKey), SecurityAlgorithms.RsaSha256);
var claims = new List<Claim>(8)
{
new Claim(JwtRegisteredClaimNames.Sub, "firebase-adminsdk-vifbe#your-service.iam.gserviceaccount.com"),
};
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.CreateEncodedJwt(
"firebase-adminsdk-vifbe#your-service.iam.gserviceaccount.com",
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
new ClaimsIdentity(claims),
DateTime.UtcNow.AddSeconds(-60),
DateTime.UtcNow.AddSeconds(10 * 60),
DateTime.UtcNow,
_credentials
);

how to add claims in jwt using jose-jwt

i am using jose jwt library to creating jwt token, i am not sure how i can i use the claims tag in the payload. i want to store user name and some other data related to it. below is the code which i am using to generate code
byte[] secretKey = Base64UrlDecode("-----BEGIN PRIVATE KEY-----");
DateTime issued = DateTime.Now;
DateTime expire = DateTime.Now.AddHours(10);
var payload = new Dictionary<string, object>()
{
{"iss", "service email"},
{"aud", "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"},
{"sub", "service email"},
{"iat", ToUnixTime(issued).ToString()},
{"exp", ToUnixTime(expire).ToString()}
};
string token = JWT.Encode(payload, secretKey, JwsAlgorithm.HS256);
return token;
The JWT specification talks about three types of claims: Registered, Public and Private.
Registered
The usual ones such as iss, sub, exp, etc.
Public claims
The IANA JWT Claims Registry is used to specify the claims that should be used publicly to standardize them between services. These contains lots of useful ones such as name, email, address, etc.
Private claims
If you are only using your token within your own application or between known applications you could actually add whatever claims you want.
It might be a good idea to avoid using claims from the IANA JWT Claims Registry for other purposes though (ie don't use name to store the users username).
So in your case your code could simply be like this to add the username (with the claim from the IANA registry)
byte[] secretKey = Base64UrlDecode("-----BEGIN PRIVATE KEY-----");
DateTime issued = DateTime.Now;
DateTime expire = DateTime.Now.AddHours(10);
var payload = new Dictionary<string, object>()
{
{"iss", "service email"},
{"aud", "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"},
{"sub", "service email"},
{"iat", ToUnixTime(issued).ToString()},
{"exp", ToUnixTime(expire).ToString()},
{"preferred_username", "MyAwesomeUsername"}
};
string token = JWT.Encode(payload, secretKey, JwsAlgorithm.HS256);
return token;
Though if it's only for internal use I would probably go with just username or usr myself.
Another thing to remember (and that many get wrong) is that JWT isn't encrypting anything. The content is base64 encoded but anyone that get hold of your token can read everything in it. So make sure to not put anything sensitive in them if there is even a slight chance that a user can see them.

Categories