One of the biggest problem at Key Management is: How to distribute keys in a security way. HMAC relies on sharing the key between many projects. To accomplish it NetDevPack.Security.Jwt
use Public Key Cryptosystem to generate your keys. So you can share you public key at https://<your_api_adrress>/jwks
!
The goal of this project is to help your application security by Managing your JWT.
- Auto create RSA or ECDsa keys
- Support for JWE
- Support public
jwks_uri
endpoint with your public key in JWKS format - Extensions for your client API's to consume the JWKS endpoint. See more at NetDevack.Security.JwtExtensions
- Auto rotate key every 90 days (Following NIST Best current practices for Public Key Rotation)
- Remove old private keys after key rotation (NIST Recommendations)
- Use recommended settings for RSA & ECDSA (RFC 7518 Recommendations)
- Uses random number generator to generate keys for JWE with AES CBC (dotnet does not support RSA-OAEP with Aes128GCM)
- By default Save keys in same room of ASP.NET DataProtection (The same place where ASP.NET save the keys to to cryptograph MVC cookies)
It generates Keys way better with RSA and ECDsa algorithms. Which is most recommended by RFC 7518.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://www.devstore.academy",
ValidAudience = "NetDevPack.Security.Jwt.AspNet"
};
});
builder.Services.AddAuthorization();
builder.Services.AddJwksManager().UseJwtValidation();
public AuthController(IJwtService jwtService)
{
_jwtService = jwtService;
}
private string GenerateToken(User user)
{
var key = _jwtService.GetCurrentSigningCredentials(); // (ECDsa or RSA) auto generated key
var handler = new JsonWebTokenHandler();
var now = DateTime.Now;
var descriptor = new SecurityTokenDescriptor
{
Issuer = "https://www.devstore.academy", // <- Your website
Audience = "NetDevPack.Security.Jwt.AspNet",
IssuedAt = now,
NotBefore = now,
Expires = now.AddMinutes(60),
Subject = new ClaimsIdentity(FakeClaims.GenerateClaim().Generate(5)),
SigningCredentials = await service.GetCurrentSigningCredentials()
};
return tokenHandler.WriteToken(token);
}
- JWT Key Management for .NET - Generate and auto rotate Cryptographic Keys for your Jwt
- đĄī¸ What is
- âšī¸ Installing
- â¤ī¸ Generating Tokens
- âī¸ Validating Token (Jws)
- â Multiple API's - Use Jwks
- đž Store
- Samples
- Changing Algorithm
- IdentityServer4 - Auto jwks_uri Management
- Why
- License
The JSON Web Key Set (JWKS) is a set of keys which contains the public keys used to verify any JSON Web Token (JWT) issued by the authorization server. The main goal of this component is to provide a centralized store and Key Rotation of your JWK. It also provide features to generate best practices JWK. It has a plugin for IdentityServer4, giving hability to rotating jwks_uri every 90 days and auto manage your jwks_uri.
If your API or OAuth 2.0 is under Load Balance in Kubernetes, or docker swarm it's a must have component. It work in the same way DataProtection Key of ASP.NET Core.
This component generate, store and manage your JWK. It keep a centralized store to share between your instances. By default after a 3 months a new key will be generated.
You can expose the JWK through a JWKS endpoint and share it with your API's.
At your API install NetDevPack.Security.Jwt
:
dotnet add package NetDevPack.Security.Jwt
Or via the .NET Core command line interface:
dotnet add package NetDevPack.Security.Jwt
Go to your startup.cs
and change Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddJwksManager().UseJwtValidation();
}
Usually we say Jwt. But in most cases we are trying to create a Jws.
public AuthController(IJwtService jwtService)
{
_jwtService = jwtService;
}
private string GenerateToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var currentIssuer = $"{ControllerContext.HttpContext.Request.Scheme}://{ControllerContext.HttpContext.Request.Host}";
var key = _jwtService.GetCurrentSigningCredentials(); // (ECDsa or RSA) auto generated key
var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
{
Issuer = currentIssuer,
Subject = identityClaims,
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = key
});
return tokenHandler.WriteToken(token);
}
Use the same service to get the current key and validate the token.
public AuthController(IJwtService jwtService)
{
_jwtService = jwtService;
}
private string ValidateToken(string jwt)
{
var handler = new JsonWebTokenHandler();
var currentIssuer = $"{ControllerContext.HttpContext.Request.Scheme}://{ControllerContext.HttpContext.Request.Host}";
var result = handler.ValidateToken(jwt,
new TokenValidationParameters
{
ValidIssuer = currentIssuer,
SigningCredentials = _jwtService.GetCurrentSigningCredentials()
});
result.IsValid.Should().BeTrue();
}
One of the biggest problem at Key Management is: How to distribute keys in a security way. HMAC relies on sharing the key between many projects. To accomplish it NetDevPack.Security.Jwt
use Public Key Cryptosystem to generate your keys. So you can share you public key at https://<your_api_adrress>/jwks
!
Peace of cake đ
Install NetDevPack.Security.Jwt.AspNetCore
in your API that emit JWT Tokens. Change your Startup.cs:
public void Configure(IApplicationBuilder app)
{
app.UseJwksDiscovery().UseJwtValidation();
}
Generating the token:
private string EncodeToken(ClaimsIdentity identityClaims)
{
var tokenHandler = new JwtSecurityTokenHandler();
var currentIssuer = $"{ControllerContext.HttpContext.Request.Scheme}://{ControllerContext.HttpContext.Request.Host}";
var key = _jwksService.GetCurrentSigningCredentials();
var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
{
Issuer = currentIssuer,
Subject = identityClaims,
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = key
});
return tokenHandler.WriteToken(token);
}
Then at your Client API, which need to validate Jwt, install NetDevPack.Security.JwtExtensions
. Then change your Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true; // keep the public key at Cache for 10 min.
x.IncludeErrorDetails = true; // <- great for debugging
x.SetJwksOptions(new JwkOptions("https://localhost:5001/jwks"));
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
}
The Controller
:
[Authorize]
public class IdentityController : ControllerBase
{
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
Done đ!
By default NetDevPack.Security.Jwt
are stored in same place where ASP.NET Core store their Cryptographic Key Material. We use the IXmlRepository.
So every change you made at DataProtection it will apply
You can override the default behavior by adding another provider and control it under your needs.
The NetDevPack.Security.Jwt
package provides a mechanism for storing yor Keys to a database using EntityFramework Core.
Install
Install-Package NetDevPack.Security.Jwt.Store.EntityFrameworkCore
Or via the .NET Core command line interface:
dotnet add package NetDevPack.Security.Jwt.Store.EntityFrameworkCore
Add ISecurityKeyContext
to your DbContext:
class MyKeysContext : DbContext, ISecurityKeyContext
{
public MyKeysContext(DbContextOptions<MyKeysContext> options) : base(options) { }
// This maps to the table that stores keys.
public DbSet<SecurityKeyWithPrivate> DataProtectionKeys { get; set; }
}
Then change your confinguration at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddJwksManager().PersistKeysToDatabaseStore<MyKeysContext>();
}
Done!
The NetDevPack.Security.Jwt
package provides a mechanism for storing yor Keys to filesystem.
Install
Install-Package NetDevPack.Security.Jwt.Store.FileSystem
Or via the .NET Core command line interface:
dotnet add package NetDevPack.Security.Jwt.Store.FileSystem
Now change your startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddJwksManager().PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys\"));
}
There are few demos here
It's possible to change default Algorithm at configuration routine.
build.Services.AddJwksManager(o =>
{
o.Jws = Algorithm.Create(DigitalSignaturesAlgorithm.RsaSsaPssSha256);
o.Jwe = Algorithm.Create(EncryptionAlgorithmKey.RsaOAEP).WithContentEncryption(EncryptionAlgorithmContent.Aes128CbcHmacSha256);
});
By default it uses recommended algorithms by RFC7518
build.Services.AddJwksManager(o =>
{
o.Jws { get; set; } = Algorithm.Create(AlgorithmType.RSA, JwtType.Jws);
o.Jwe { get; set; } = Algorithm.Create(AlgorithmType.RSA, JwtType.Jwe);
}
The Algorithm object has a list of possibilities.
Algorithms:
Shortname | Name |
---|---|
HS256 | Hmac Sha256 |
HS384 | Hmac Sha384 |
HS512 | Hmac Sha512 |
RS256 | Rsa Sha256 |
RS384 | Rsa Sha384 |
RS512 | Rsa Sha512 |
PS256 | Rsa SsaPss Sha256 |
PS384 | Rsa SsaPss Sha384 |
PS512 | Rsa SsaPss Sha512 |
ES256 | Ecdsa Sha256 |
ES384 | Ecdsa Sha384 |
ES512 | Ecdsa Sha512 |
Algorithms options:
Shortname | Key Management Algorithm |
---|---|
RSA1_5 | RSA1_5 |
RsaOAEP | RSAES OAEP using |
A128KW | A128KW |
A256KW | A256KW |
Encryption options
Shortname | Content Encryption Algorithm |
---|---|
Aes128CbcHmacSha256 | A128CBC-HS256 |
Aes192CbcHmacSha384 | A192CBC-HS384 |
Aes256CbcHmacSha512 | A256CBC-HS512 |
NetDevPack.Security.Jwt
provides IdentityServer4
key material. It auto generates and rotate key.
First install
Install-Package NetDevPack.Security.Jwt.IdentityServer4
Or via the .NET Core command line interface:
dotnet add package NetDevPack.Security.Jwt.IdentityServer4
Go to Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients());
services.AddJwksManager().IdentityServer4AutoJwksManager();
}
If you wanna use Database, follow instructions to DatabaseStore instead.
When creating applications and APIs in OAuth 2.0 or simpling Signing a JWT Key, many algorithms are supported. While there a subset of alg's, some of them are considered best practices, and better than others. Like Elliptic Curve with PS256 algorithm. Some Auth Servers works with Deterministic and other with Probabilist. Some servers like Auth0 doesn't support more than one JWK. But IdentityServer4 support as many as you configure. So this component came to abstract this layer and offer for your application the current best practies for JWK.
When working in containers with Kubernetes or Docker Swarm, if your application scale them you became to have some problems, like DataProtection Keys that must be stored in a centralized place. While isn't recommended to avoid this situation Symmetric Key is a way. So this component, like DataProtection, provide a Centralized store for your JWKS.
Many developers has no clue about which Algorithm to use for sign their JWT. This component uses Elliptic Curve with ECDSA using P-256 and SHA-256 as default. It should help to build more secure API's and environments providing JWKS management.
NetDevPack.Security.Jwt is Open Source software and is released under the MIT license. This license allow the use of NetDevPack.Security.Jwt in free and commercial applications and libraries without restrictions. p