Donald's Bacon Bytes

bytes of information as tasty as bacon

Create JWT with a Private RSA Key

Topic: Development | Tags: ,

Aug
11
2016

Recently I ran into a challenge at work and that I had hard time figuring out how to solve. We have an authenticate server written in NodeJS. It signs a user into our application and creates a JWT. This application signs the JWT with a PEM file which ONLY has a Private RSA Key. Our C# API can use the corresponding certificate file (.cer) to validate the token when it is passed in each API call.

This has been working great. For reasons I’m not going to get into here, we had to come up with a way to create a token from our Asp.Net admin that would allow someone to login as a specific user without logging in. We are doing this for a demo type of user account. The challenge was that I couldn’t find any good examples that would allow me to create the JWT using only the PEM file. I found hints here and there but nothing exact.

The solution I came up with was to use a combination of Bouncy Castle and Jose JWT. I used Bouncy Castle to create the signing key and then Jose JWT to encode the token.

public static string CreateToken(List<Claim> claims, string certificateName, string rootFolder, string certificateFolder = "Certificates")
 { 
 string path = Path.Combine(rootFolder, certificateFolder, certificateName);
 string pemString = File.ReadAllText(path);
 string jwt = string.Empty;
 AsymmetricCipherKeyPair keyPair;

 using (StreamReader sr = new StreamReader(path))
 {
 PemReader pr = new PemReader(sr);
 keyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
 } 
 
 RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);

 using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
 {
 rsa.ImportParameters(rsaParams);
 Dictionary<string, object> payload = claims.ToDictionary(k => k.Type, v => (object)v.Value);
 jwt = Jose.JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256);
 }

 return jwt;
 }

As you can see, this is a somewhat generic method that takes in the path to the certificate along with the claims and then creates the JWT out of that information.

Anyway, I hope this can help others.

If you have other suggestions, I’d love to hear them in the comments.

15 Responses to “Create JWT with a Private RSA Key”

Show / Hide Comments
  1. Marlon Chavarria says:

    Hi

    Do you have a complete working example?

    That would be very helpful!

    Thanks

    • donaldhansen says:

      Sorry for not responding sooner. I get so many spam comments that I didn’t notice I had a real comment. I don’t have a working example that can be included. Besides the method in my post, you need to include some dependencies. Here are the using statements I have in my code:

      using Org.BouncyCastle.Crypto;
      using Org.BouncyCastle.Crypto.Parameters;
      using Org.BouncyCastle.OpenSsl;
      using Org.BouncyCastle.Security;
      using System;
      using System.Collections.Generic;
      using System.Configuration;
      using System.IO;
      using System.Linq;
      using System.Security.Claims;
      using System.Security.Cryptography;

      Create a class called “TokenManager” or something like that, add the using statements along with the method in my article and you should be good to go.

  2. Matteo says:

    I’m new trying to generate JWTs on ASP.NET Core in order to consume them into firebase called from a web app (using custom token auth in firebase).
    This means I have a RS256 private key in string format “—–BEGIN RSA PRIVATE KEY—–… —–END RSA PRIVATE KEY—–” only.
    Here:
    https://github.com/dvsekhvalnov/jose-jwt#if-you-have-only-rsa-private-key
    they point to this article saying this can help in this situation… but I don’t understand how to call your CreateToken function, where to place it in my ASP.NET Core MVC conctroller code and which libraries need to be imported (dependencies).

    Can you please kindly provide an example for this use-case?

    Thanks very much, best regards, Matteo

    • donaldhansen says:

      Wow, I had no idea the Jose project was linking to this article. I just replied to a comment from Marlon that mentions how I use this method. Create a class for the method in my article, add the using statements (be sure to import any of the dependencies you do not already have) and then add my method. Pass in the path to your key along with the claims you want to be part of your token and you will then get a jwt token returned back to you.

      • Matteo says:

        Thanks. I’ll try soon.
        Just a question: can I avoid the file (path, etc.) management and put direct the private key text inside the pemString variable?
        best regards

        • donaldhansen says:

          Sure. I don’t see why not. We have it in a file since it is used by 2 different apps and we wanted to make it easy to update if we ever needed to.

          • Matteo says:

            Donald, here I am with 1st feedback.
            1. Bouncy Castle to use: Portable.BouncyCastle
            2. Where can I find DotNetUtilities?

            Any suggestion? Thanks a lot

  3. donaldhansen says:

    Matteo – Seems I can’t reply to your last comment. Thanks a lot WordPress! haha.

    Anyway, I’m not using the Portable version of Bouncy Castle. Search for BouncyCastle and I’m using version 1.8.1 of their main package.

    DotNetUtilities is part of BouncyCastle and is in the namespace Org.BouncyCastle.Security

  4. Matteo says:

    Hi Donald, new feedback:
    I tried all the BouncyCastle libs available in NuGet compatible with .NET Core but no one has the DotNetUtilities class included. Thus I created a new one by myself and copy&paste the original code from the Org.BouncyCastle.Security of the principal lib (that VisualStudio refuse because not compatible with CoreNet); within the class there were lot of not used method with plenty of external unknown references, thus I removed all the method but the only one used by your code.

    Right now the project builds fine, but I get a runtime error on the following row:
    keyPair = (AsymmetricCipherKeyPair)pr.ReadObject();

    and this is the detailed reason:

    Unable to cast object of type ‘Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters’ to type ‘Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair’
    (where the 1st is the type returned by pr.ReadObject() and the 2nd is the “keyPair” type.
    Please consider that I feed the pr object with a StringReader(pemString) instead of the original StreamReader(path), because perString contains the private key in the format “—-BEGIN RSA PRIVATE KEY—-\n…\n—-END RSA PRIVATE KEY—-\n”

    Please, do you have any suggestion how to make this really working?
    Thanks very much

  5. Matteo says:

    WOW, finally I’ve found the way to really generate the first JWT with RS256 in .NET Core starting from the private key in the format “—-BEGIN RSA PRIVATE KEY—-…—-END RSA PRIVATE KEY—-”.

    Here for everybody the slight changes applied to your code in order to have it working:

    1. I tried many BouncyCastle libs for NetCore, no one has the DotNetUtilities class available. Finally I used the “BouncyCastle.NetCore 1.8.1.2” ( https://www.nuget.org/packages/BouncyCastle.NetCore )

    2. I created a new empty file DotNetUtilities.cs and copied&pasted the original class ( https://github.com/neoeinstein/bouncycastle/blob/master/crypto/src/security/DotNetUtilities.cs ), then removed all the content but the “ToRSAParameters(RsaPrivateCrtKeyParameters)” method (the only one you use – many other methods have unresolved dependencies)

    3. I created a new empty file TokenManager.cs with inside all the “using” you explained above to Marlon and then a class with the same name.

    4. Inside the TokenManager class I put the following code (it’s your code where I replaced the input params related to the pem file with the pem private key string and adjusted the code inside to make it working):

    public static string CreateToken(List claims, string privateKey)
    {
    string jwt = string.Empty;
    RsaPrivateCrtKeyParameters key;

    using (StringReader sr = new StringReader(privateKey)) //here used StringReader
    {
    PemReader pr = new PemReader(sr);
    key = (RsaPrivateCrtKeyParameters)pr.ReadObject(); //no more keyPair because I just have the privKey only
    }

    RSAParameters rsaParams = DotNetUtilities.ToRSAParameters(key);

    using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
    {
    rsa.ImportParameters(rsaParams);
    Dictionary payload = claims.ToDictionary(k => k.Type, v => (object)v.Value);
    jwt = Jose.JWT.Encode(payload, rsa, Jose.JwsAlgorithm.RS256);
    }

    return jwt;
    }

    Finally the first valid JWT token has been released and succesfully consumed by Firebase using firebase.auth().signInWithCustomToken(token) from js!
    PS: last thing to study to make it perfect for firebase is to make the List not flat (list of all claims) but insert a single claim called “claims” that is a List itself (nested list).

    Thanks very much, best regards

    • Danny says:

      I’ve followed along with these instructions but I am getting a runtime error similar to that in your previous post, but it’s

      System.InvalidCastException Unable to cast object of type ‘Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair’ to type ‘Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters’.

      for the line key = (RsaPrivateCrtKeyParameters)pr.ReadObject();

      The only difference is that I’m using BouncyCastle.NetCore 1.8.1.3.

      Any idea how to resolve?

  6. Matteo says:

    PS: managed the nested claim-list: when the claim type is “claims”, I consider the claim value as a serialized json and deserialize it in the payload.
    Just replaced the command in your code:
    Dictionary payload = claims.ToDictionary(k => k.Type, v => (object)v.Value);
    with:
    Dictionary payload = claims.ToDictionary(k => k.Type, v => (v.Type == “claims”) ? (object)Newtonsoft.Json.JsonConvert.DeserializeObject(v.Value) : (object)v.Value);

    –that’s all–thanks bye

    • donaldhansen says:

      Awesome. Nice work Matteo!!

      I haven’t had a chance to do much with .Net Core but when I do, this will come in handy.

      Thanks for posting your code.

  7. Matteo says:

    It’s still open the point how to validate the token using the public key in PEM format in C# (in case the 1st server emit the jwt signed with the private key -as above-and a 2nd service wants to validate server side in c# the token)…
    Any idea how to use the “public only” key in PEM format to validate the signature in c#?
    Thanks a lot, best regards

  8. Alexandr Marchenko says:

    Thank you Donald for your post and Matteo for further investigation, I at last were able to create token that is acceptable from firebase in dotnet core

    in case someone will fight that also here is code:

    http://mac-blog.org.ua/dotnet-core-firebase-jwt/

Leave a Comment