Earlier this year I blogged about post-quantum cryptography in .NET using Dilithium and Kyber. This was then followed by another post, which showed how Dilithium can be wired into a popular .NET Identity Server, Duende Identity Server, for token signing purposes.
Today I would like to announce Strathweb.Dilithium, a set of .NET helper libraries to facilitate working with Dilithium in ASP.NET Core projects.
Overview π
While it is possible to proceed at integrating Crystals-Dilithium by hand, the ASP.NET Core ecosystem has its own specific extensibility points, commonly used set of crypto-primitives and conventions, which makes it surprisingly difficult to plug in a cryptography scheme that is not part of .NET itself. And that was the motivation behind Strathweb.Dilithium - to facilitate and streamline the integration of Dilithium signature scheme into ASP.NET Core projects - both for the purposes of token signing and their validation, all while avoiding the clunky integration aspects.
The algorithm implementations come from the excellent BouncyCastle and supports Dilithium2, Dilithium3 and Dilithium5 parameter sets. The libraries are intended to be used as the ASP.NET Core implementation of JOSE and COSE Encoding for Dilithium IETF draft.
There are a total of three libraries used for different purposes (as described below), and the overall experience should be as close to plug and play as possible. Working with Dilithium should be indistinguishable from other signing schemes. All the packages are already published on Nuget.
Strathweb.Dilithium.IdentityModel π
This is the base package on top of which other features can be built. Contains integration of Dilithium into the ecosystem of Microsoft.IdentityModel.Tokens. Those are:
DilithiumSecurityKey
, which implementsAsymmetricSecurityKey
abstract classDilithiumSignatureProvider
, which implementsSignatureProvider
abstract classDilithiumCryptoProviderFactory
, which extends the defaultCryptoProviderFactory
A new instance of a Dilithium public-private pair can be created by using the main constructor that takes in the algorithm (CRYDI2
, CRYDI3
or CRYDI5
) identifier.
var securityKey = new DilithiumSecurityKey("CRYDI3");
The encoded private and public keys can then be read using the relevant properties:
byte[] publicKey = securityKey.PublicKey;
byte[] privateKey = securityKey.PrivateKey;
They can also be exported out of process (e.g. using base64url encoding) and later used to re-initialize the key:
var securityKey = new DilithiumSecurityKey("CRYDI3", publicKey, privateKey);
The private key is optional - in which case the key can still be used for signature validation but not longer for signing.
It is also possible to export the key to JSON Web Key format (where it is possible to decide whether the private key should be included or not):
JsonWebKey jwk = securityKey.ToJsonWebKey(includePrivateKey: true);
Such a JWK can be serialized, persisted or published, and later re-imported:
var securityKey = new DilithiumSecurityKey(jwk);
Depending on whether the JWK was exported with the private key or not, the instance of DilithiumSecurityKey
will be suitable for signing or only for validation of signatures.
Strathweb.Dilithium.DuendeIdentityServer π
Add-on to Duende IdentityServer, which allows for registering a DilithiumSecurityKey
as a valid token signing credential. Once configured, the Dilithium key can be used for token signing for API resources that are flagged as compatible with the Dilithium algorithms. The public key is also going to get announced with the JWKS document.
Possible usages:
- Create an ephemeral public-private pair
This pair will be discarded upon application shutdown.
builder.Services.AddIdentityServer()
.AddDilithiumSigningCredential(new DilithiumSecurityKey("CRYDI3")) // new key per startup
- Load a Dilithium key from a JSON Web Key format
It is possible to manually load JWK (Microsoft.IdentityModel.Tokens.JsonWebKey
) from some source, such as a key vault, and then use it to initialize the DilithiumSecurityKey
:
// load the JWK from somewhere e.g. KeyVault or filesystem
builder.Services.AddIdentityServer()
.AddDilithiumSigningCredential(new DilithiumSecurityKey(jwk)) // key from the JWK
// continue with the rest of IDentity Server configuration
Alternatively, it can also be loaded from the file system (using a path relative to the current directory or an absolute one):
builder.Services.AddIdentityServer()
.AddDilithiumSigningCredential(pathToDilithiumJWK) // key from the JWK on the filesystem
// continue with the rest of IDentity Server configuration
- Load a Dilithium key from byte array public/private key representations
// load the public key and private key from somewhere e.g. KeyVault or filesystem
byte[] privateKey = ...
byte[] publicKey = ...
builder.Services.AddIdentityServer()
.AddDilithiumSigningCredential(new DilithiumSecurityKey("CRYDI3", publicKey, privateKey)) // key from the JWK
// continue with the rest of IDentity Server configuration
Once registered, the Identity Server will announce the public part of the Dilithium key in the JWKS document. Other non-post quantum keys are allowed to co-exist. Example:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "30F4....",
"e": "AQAB",
"n": "scmPFy....",
"alg": "RS256"
},
{
"kty": "MLWE",
"use": "sig",
"kid": "A574....",
"alg": "CRYDI3",
"x": "OMjMS...."
}
]
}
The JWT tokens issued by the Identity Server will contains the "alg": "CRYDI3"
in the header; otherwise the token will be indistinguishable from the other tokens.
Strathweb.Dilithium.AspNetCore π
Add-on for Microsoft.AspNetCore.Authentication.JwtBearer package, allowing for enabling Dilithium-signed JWT token validation for the Bearer
scheme.
Usage:
builder.Services.AddAuthentication().AddJwtBearer(opt =>
{
// all the usual necessary configuration such as authority or audience
// omitted for brevity
// enable Dilithium tokens
opt.ConfigureDilithiumTokenSupport();
});
When Dilithium token support is enabled, the extension takes over the management of JSON Web Keys fetched from the trusted authority. Those are cached for 24h, but this can be changed in the configuration.
By default any other tokens from the trusted authority are allowed as well. However, it is also possible to restrict the API to only accept Dilithium based signing keys.
builder.Services.AddAuthentication().AddJwtBearer(opt =>
{
// all the usual necessary configuration such as authority or audience
// omitted for brevity
// enable Dilithium tokens
opt.ConfigureDilithiumTokenSupport(dopt => dopt.AllowNonMlweKeys = false;);
});