JWT Signing using ECDSA in .NET Core

Scott Brady
Scott Brady
C# ・ Updated August 2021 23 August 2021

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:

ECDSA curves defined in RFC 7518 (JWA)
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:

Content is licensed under CC BY 4.0. Remember, don't copy and paste code written by strangers on the internet.