The Elliptic Curve Digital Signing Algorithm (ECDSA) is generally a better signing algorithm than your traditional RSA. ECDSA is generally harder to crack, resulting in much shorter keys and signatures for a similar level of security. For example, a 256-bit key Elliptic Curve (EC) key provides the same security as a 3072-bit RSA key.
While better signing algorithms are available, ECDSA has seen a rise in popularity thanks to an update in security to many mainstream OpenID Connect and SAML identity providers and a requirement of its usage in Open Banking and eiDAS.
In this article, you’ll learn how to use ECDSA for signing JSON Web Tokens (JWTs) in .NET using the ECDsa
and ECDsaSecurityKey
classes from Microsoft.IdentityModel
.
Creating a key with ECDsa
To start, you’ll need an EC key.
You could generate an EC key using OpenSSL, but for now, let’s generate an in-memory key by creating .NET’s representation of ECDSA, aptly named ECDsa
.
You’ll need to know what curve you’re using to generate an EC key since this will affect how your key gets generated. In this example, you’ll be signing JWTs with ES256, which means you’ll need a key that uses NIST’s P-256 curve (also known as secp256r1).
var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
Using ECDsa
this way creates a new instance of ECDsa
, containing your private key and public key.
If you already have an EC key, you’re welcome to use that by loading the EC key and creating an ECDsa
object.
Just make sure you use the correct curve.
Choosing the right curve
The JSON Web Algorithms (JWA) spec originally defined three algorithms that use ECDSA:
JOSE Algorithm | Curve | Hashing Algorithm |
---|---|---|
ES256 | P-256 (secp256r1) | SHA-256 |
ES384 | P-384 (secp384r1) | SHA-384 |
ES512 | P-521 (secp521r1 - 521 is not a typo) | SHA-512 |
.NET supports these three curves out of the box, and they are available on the NamedCurves
static class.
For example ES256 would use ECCurve.NamedCurves.nistP256
.
If you want to use a curve not supported by .NET, or a curve that is not defined by NIST, for example, ES256K, you’ll need to create your own implementation of ICryptoProvider.
Microsoft.IdentityModel’s JsonWebTokenHandler
In this article, you’ll use the JsonWebTokenHandler
found in Microsoft.IdentityModel.JsonWebTokens
, rather than the older JwtSecurityTokenHandler
found in System.IdentityModel.Tokens.Jwt
.
This newer library has a much nicer API to work with and does not require you to use the ClaimsPrincipal
and ClaimsIdentity
objects everywhere.
So, to continue, you’ll need the relevant dependencies from nuget:
dotnet add package Microsoft.IdentityModel.JsonWebTokens
I wrote this article using version 6.12; however, the APIs you’ll see have been available since version 5.5.
Signing a JWT using ECDSA in .NET
To create a JWT, you’ll need to create a JsonWebTokenHandler
and SecurityTokenDescriptor
, with which you can then call the handler’s CreateToken
method.
var now = DateTime.UtcNow;
var handler = new JsonWebTokenHandler();
string token = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = "me",
Audience = "you",
NotBefore = now,
Expires = now.AddMinutes(30),
IssuedAt = now,
Claims = new Dictionary<string, object> { { "sub", "123" } },
SigningCredentials = new SigningCredentials(new ECDsaSecurityKey(key), "ES256")
});
This is your typical code for generating a JWT; however, it’s how you are creating the SigningCredentials
that makes the difference.
For your SigningCredentials
, you are passing in an ECDsaSecurityKey
and the JOSE algorithm you’ll use to sign the token.
In this case, you have defined the algorithm using the hardcoded string “ES256”; however, that value is also available as a constant in the SecurityAlgorithms
class as SecurityAlgorithms.EcdsaSha256
.
ECDsaSecurityKey
is created by passing in your EC key, the ECDsa
object you created earlier, which contains your private key.
With this configuration, the handler will understand what curve and hashing algorithm to use.
JWT signed using ES256
Running the above code results in a JWT that looks like the following:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJhdWQiOiJ5b3UiLCJleHAiOjE2Mjk0Nzg4MjEsImlzcyI6Im1lIiwiaWF0IjoxNjI5NDc3MDIxLCJuYmYiOjE2Mjk0NzcwMjF9.b0_QtzcHDjutdRFSIW2ZyXWH8iEnunoxurzZdfuy-Gm2KJNn_sqU585ZBE7UxBIJXUJwl7cbwKZLXaZDf8X8yA
This JWT has a much shorter signature than the RSA alternative (RS256).
Validating a JWT signing using ECDSA in .NET
You can then validate the JWT by calling the handler’s ValidateToken
method.
var handler = new JsonWebTokenHandler();
TokenValidationResult result = handler.ValidateToken(token, new TokenValidationParameters
{
ValidIssuer = "me",
ValidAudience = "you",
IssuerSigningKey = new ECDsaSecurityKey(key)
});
var isValid = result.IsValid;
ValidateToken
takes the JWT itself and some TokenValidationParameters
, that again use an ECDsaSecurityKey
.
However, this time, it only requires the public key.
The TokenValidationResult
contains the claims from the token, but for this example, all you’re concerned about is if the token (and therefore signature) is valid or not.
Source Code
The above code is available in my GitHub samples repository, where it runs as a unit test against each NIST curve, targeting .NET Core 3.1 onwards, on both Linux and Windows.
To learn more about JWTs in .NET, check out my other articles:
- How to choose the best JWT signing algorithm
- How to create
ECDsa
objects in .NET - How to use ECDSA in IdentityServer
- How to sign JWTs using EdDSA, RSA-PSS, and unsupported algorithms (ES256K) in .NET and .NET Core