Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core IssuerSigningKey from file for JWT Bearer Authentication

Tags:

I am struggling with the implementation (or the understanding) of signing keys for JWT Bearer Token authentication. And I hope somebody can help me or explain me what I am missunderstanding.

The last few weeks I crawled tons of tutorials and managed to get a custom Auth-Controller running which issues my tokens and managed to set up the JWT bearer authentication to validate the tokens in the header.

It works.

My problem is that all examples and tutorials either generate random or inmemory (issuer) signing keys or use hardcoded "password" strings or take them from some config file (look for "password" in the code samples).

What I mean for validation setup (in the StartUp.cs):


  //using hardcoded "password"
  SecurityKey key = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes("password"));

  app.UseJwtBearerAuthentication(new JwtBearerOptions
  {
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = new TokenValidationParameters
    {
      ValidateIssuer = true,
      ValidIssuer = "MyIssuer",
      ValidateAudience = true,
      ValidAudience = "MyAudience",
      ValidateLifetime = true,
      IssuerSigningKey = key
    }
  });

In the AuthController creating the token:


  //using hardcoded password
  var signingKey = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes("password"));
  SigningCredentials credentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

  var jwt = new JwtSecurityToken     // Create the JWT and write it to a string
  (
    issuer: _jwtTokenSettings.Issuer,
    audience: _jwtTokenSettings.Audience,
    claims: claims,
    notBefore: now,
    expires: now.AddSeconds(_jwtTokenSettings.LifetimeInSeconds),
    signingCredentials: credentials
  );

In this question they used:

RSAParameters keyParams = RSAKeyUtils.GetRandomKey();

My (current) assumptions was that in production you should not use hardcoded strings or strings from config files for the token signing keys. But instead use some certificate files??? Am I wrong?

So I tried to replace the strings with a certificate which works in the auth controller:


  //using a certificate file
  X509Certificate2 cert = new X509Certificate2("MySelfSignedCertificate.pfx", "password");
  X509SecurityKey key = new X509SecurityKey(cert);
  SigningCredentials credentials = new SigningCredentials(key, "RS256");

  var jwt = new JwtSecurityToken      // Create the JWT and write it to a string
  (
     issuer: _jwtTokenSettings.Issuer,
     audience: _jwtTokenSettings.Audience,
     claims: claims,
     notBefore: now,
     expires: now.AddSeconds(_jwtTokenSettings.LifetimeInSeconds),
     signingCredentials: credentials
  );

But there seems no way to get the validation using a certificate.


  X509Certificate2 cert = new X509Certificate2("MySelfSignedCertificate.pfx", "password");
  SecurityKey key == // ??? how to get the security key from file (not necessarily pfx)

  app.UseJwtBearerAuthentication(new JwtBearerOptions
  {
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = new TokenValidationParameters
    {
      ValidateIssuer = true,
      ValidIssuer = "MyIssuer",
      ValidateAudience = true,
      ValidAudience = "MyAudience",
      ValidateLifetime = true,
      IssuerSigningKey = key
    }
  });

Am I wrong that I should use certificates for the signing keys? How else would I change the signing keys in production when the auth controller is on a different server than the consuming/secured web api (may one time, not now)?

It seems I also miss the point (required steps) to get the answer of this question working.


Now I got it running I am still missing the point if it should be like that?


Noteworthy: File not found (after deployment to server)

For all those using this and it works when started from Visual Studio but after deployment to a server / azure it says "File not found":

read and upvote this question: X509 certificate not loading private key file on server


Noteworthy 2: one actually doesn't need it for token based authentication

The public key does not need to be on the API side. It will be retrieved via the discovery endpoint of the authentication server automatically.

like image 804
monty Avatar asked Sep 19 '17 07:09

monty


2 Answers

Oh dear, that simple:

SecurityKey key = new X509SecurityKey(cert);

Or as complete sample from above:

X509Certificate2 cert = new X509Certificate2("MySelfSignedCertificate.pfx", "password");
SecurityKey key = new X509SecurityKey(cert); //well, seems to be that simple
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = "MyIssuer",
        ValidateAudience = true,
        ValidAudience = "MyAudience",
        ValidateLifetime = true,
        IssuerSigningKey = key
     }
});
like image 169
monty Avatar answered Sep 24 '22 08:09

monty


A very important point, if you are using certificate files, that while the server requires the file with the private key, the client should only use the public key.

You do not want to be giving your private key file to anyone; they only ever need the public key.

// On client
var publicCert = new X509Certificate2("MySelfSignedCertificate.cer");
var publicKey = new X509SecurityKey(publicCert);
...
    IssuerSigningKey = publicKey

The simplest way to convert the PFX (private) to CER (public) may be to import into the Windows certificate manager, then export with the public key only.

From the command line, you can create also use PowerShell 5 (not yet in PowerShell 6):

Get-PfxCertificate -FilePath MySelfSignedCertificate.pfx | Export-Certificate -FilePath MySelfSignedCertificate.cer

Alternatively, you can install and use OpenSSL to convert it from the command line.

Note 1: As you found, once you set the Authority, the auto-discovery may be able to find the public key from the server.

Note 2: Rather than store the certificate in a file, you can also store it in the Windows certificate store, and reference it by thumbprint (both PFX and CER files can be imported).

like image 31
Sly Gryphon Avatar answered Sep 22 '22 08:09

Sly Gryphon