In this article we will see how we can create and sign a JWT token with the RS256 algorithm. This function is complementary to the validate function I posted some time ago.
Here is the Sign(...)
function that can create a RS256 signed JWT token. It makes use of the BouncyCastle library. It is available as a NuGet package with version 1.8.1.
using System; using System.Collections.Generic; using System.Text; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Newtonsoft.Json; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Pkcs; public string Sign(string payload, string privateKey) { List segments = new List(); var header = new { alg = "RS256", typ = "JWT" }; DateTime issued = DateTime.Now; DateTime expire = DateTime.Now.AddHours(10); byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None)); byte[] payloadBytes = Encoding.UTF8.GetBytes(payload); segments.Add(Base64UrlEncode(headerBytes)); segments.Add(Base64UrlEncode(payloadBytes)); string stringToSign = string.Join(".", segments.ToArray()); byte[] bytesToSign = Encoding.UTF8.GetBytes(stringToSign); byte[] keyBytes = Convert.FromBase64String(privateKey); var privKeyObj = Asn1Object.FromByteArray(keyBytes); var privStruct = RsaPrivateKeyStructure.GetInstance((Asn1Sequence)privKeyObj); ISigner sig = SignerUtilities.GetSigner("SHA256withRSA"); sig.Init(true, new RsaKeyParameters(true, privStruct.Modulus, privStruct.PrivateExponent)); sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length); byte[] signature = sig.GenerateSignature(); segments.Add(Base64UrlEncode(signature)); return string.Join(".", segments.ToArray()); }
Here are some helper functions used in the above snippet.
// from JWT spec private static string Base64UrlEncode(byte[] input) { var output = Convert.ToBase64String(input); output = output.Split('=')[0]; // Remove any trailing '='s output = output.Replace('+', '-'); // 62nd char of encoding output = output.Replace('/', '_'); // 63rd char of encoding return output; } private static byte[] Base64UrlDecode(string input) { var output = input; output = output.Replace('-', '+'); // 62nd char of encoding output = output.Replace('_', '/'); // 63rd char of encoding switch (output.Length % 4) // Pad with trailing '='s { case 0: break; // No pad chars in this case case 1: output += "==="; break; // Three pad chars case 2: output += "=="; break; // Two pad chars case 3: output += "="; break; // One pad char default: throw new System.Exception("Illegal base64url string!"); } var converted = Convert.FromBase64String(output); // Standard base64 decoder return converted; }
The helper functions are the same ones found in the validate function.
This function is based on the code snippet found in this SO question.
Update 1: You can check this post here, where I have created a C# library that manages Jwt tokens.
Update 2: If you are having trouble making your keys work, have a look in my Check your RSA private and public keys post and make sure to check the Additional Resources section as well
Murali says
Where is the Base64Encoding() definition?
_tasos says
Hi Murali, you can find the helper functions in this post.
Fabio says
Hi, can you give a simple example of how you generate the private key?
_tasos says
Hi Fabio, I used openssl to generate the private and public keys
Private key:
Public key:
Check also this post: OpenSSL Command Cheatsheet
Raju Chapagain says
Base64UrlEncode function is not there? Could you please explain whether it is built in function and I am missing to include any reference or it is custom function and You have missed to put in your code.
_tasos says
Hi Raju, thanks for noticing! I updated the code accordingly.
Rushal Maksane says
How can i verify using public key? with RSA384 algorithm? Any help is appreciated.
_tasos says
Hi Rushal,
You can use the code here with RS384. Just replace 256 to 384 and it should work. Also check here to see how you can verify using the public key.
Romeo Arnoza says
Hi Tasos,
I’m encountering below error on this line:
Eduardo Nardi says
Hi Romeo,
Though an older question, I’d gotten the same error, I don’t know why, I think that some deprecated code.
I changed this:
For this:
And it worked fine for me.
_tasos says
Hi Eduardo,
Thanks for your reply.
I tried your change and I am getting the following error when calling
Which version of BouncyCastle are you using? I checked my code with 1.8.2 and 1.8.5 and it is working without issues.
Eduardo Nardi says
Hi _tasos,
Before all, congratulation for your post, it help me a lot !!!
I’m using 1.8.4 and getting error invalid cast, I’ll try 1.8.5 version.
_tasos says
Thank you, I am glad you found it useful!
Now, regarding the issue, I am guessing that the private key might be different, thus the need to load it with a different way. I am wondering if you have the openssl command that generates you private key so that I can test against my code.
_amy says
byte[] keyBytes = Convert.FromBase64String(privateKey);
I’m encountering error on this line. My private key is in pem format but it says unable to convert using base 64. How did you guys resolve this?
Raheel says
Hi _tasos,
How to define payload with multiple values like:
payload = {
“iss”: issuer,
“sub”: client_id,
“aud”: aud
}
_tasos says
Hi Raheel,
The payload can be anything. If you need it to be a JSON object, you’ll need to serialize it. Have a look here for examples.
Alex Lynn Haberer says
Hi,
I’m getting a “unknown tag 13 encountered”, online it seem the private key needs to be in DER format, I only have a private key —Begin…End— I tried removing those lines along with “/n” but that then give me “Extra data found at the end…”
It seems to be failing at var privKeyObj = Asn1Object.FromByteArray(keyBytes);
_tasos says
Hey Alex,
Sadly, I am not familiar with the DER format. The line that fails indicates that is a compatibility issue. You may be able to convert it to PEM format. Check out this SO question, maybe you be able to convert it.
Rehman says
Hi,
do you have sample code to decode the JWT created using above code.
Thanks
admin says
Hi Rehman,
You can checkout this post
Ken Riley says
This was incredibly helpful to me as I have been struggling to get a proper signed JWT using the built in .net crypto functions. My company is using an older version of .net and for reasons unknown none of the online info on how to assymetrically sign JWT’s were working. I used the above code and generated the keys using the link above and it all worked first time. Thanks so much!
amr says
Hello,
Have you encountered such a problem?
Unable to cast object of type ‘Org.BouncyCastle.Asn1.DerSequence’ to type ‘Org.BouncyCastle.Asn1.DerInteger’
If yes, how did you get over it?