how to sign bytes using my own rsa private key using rs256 algorithm?
Asked Answered
M

4

2

I have my own private key string, i.e.

-----BEGIN RSA PRIVATE KEY-----

MIICXAIBAAKBgQCSAYYgzvGTww....
....
....
.....
3yUMYj9oYzqdrRHP0XgD0cEEvyqPBwLaNsRdFwy5qTiHjj0f+ZWHQWmqcoLmmpzyZEbIvQm/VhbjRF6iKG4WZ9Hfa7ntYRNGdWgD/KMIeZI=

-----END RSA PRIVATE KEY-----

Now, I need to sign my claim set using this private key in C# to generate JWT payload.

I have written the following code:

var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.Now;

var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;

var payload = new
{
    iss = email,
    prn = prn,
    scope = "scope",
    aud = "https://example.com",
    exp = exp,
    iat = iat
};

var segments = new List<string>();
var header = new { typ = "JWT", alg = "RS256" };

byte[] headerBytes = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(header));
byte[] payloadBytes = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(payload));

segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));

var stringToSign = string.Join(".", segments.ToArray());

var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

Now I need these bytesToSign to be signed by my private key as I mentioned above using the rs256 alogorithm. Can anyone help?

I've updated my code as per the following:

var pemprivatekey = OpenSSLKey.DecodeOpenSSLPrivateKey(privateKey);
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();

if (pemprivatekey != null)
{
    rsaProvider = OpenSSLKey.DecodeRSAPrivateKey(pemprivatekey);
}

byte[] signature = rsaProvider.SignData(bytesToSign, "SHA256");

segments.Add(Base64UrlEncode(signature));

return string.Join(".", segments.ToArray());

and generated JWT token. Please let me know where I made a mistake as its not correct one: when I pass it to the API, it doesn't work and throws an error.

Minetta answered 18/9, 2014 at 9:32 Comment(4)
Start here come back if you get stuck. For others, RS256 seems to be a JSON specified name for RSASSA-PKCS-v1_5 using SHA-256.Nursling
I tried using OpenSSLKey, it decoded RSA Private key as well and I am able to encode my bytes for generating JWT token, but when I pass to APIs, it states error as invalid JWT token, Any help? , I have added updated code in above question itself.Minetta
Seems like you got ahead, but I'm not knowledgeable about the JWT format myself, unfortunately.Nursling
Also see Signing and verifying signatures with RSA C#, how to sign bytes using my own rsa private key using rs256 algorithm?, Signing data with private key in c#, How can I sign a file using RSA and SHA256 with .NET?, Signing a string with RSA private key on .NET?, etc.Kishke
M
-5

I found some purely Javascript based solution, if it is useful to anyone. You can find the JS Libraries here.

It has resolved my requirement.

Minetta answered 27/10, 2014 at 14:34 Comment(0)
L
2

The key to this question is using JWT and Bouncy castle libraries for encoding the token and signing it respectively.

  1. JWT for encoding and decoding JWT tokens
  2. Bouncy Castle supports encryption and decryption, especially RS256 get it here

First, you need to transform the private key to the form of RSA parameters. Then you need to pass the RSA parameters to the RSA algorithm as the private key. Lastly, you use the JWT library to encode and sign the token.

    public string GenerateJWTToken(string rsaPrivateKey)
    {
        var rsaParams = GetRsaParameters(rsaPrivateKey);
        var encoder = GetRS256JWTEncoder(rsaParams);

        // create the payload according to your need
        var payload = new Dictionary<string, object>
        {
            { "iss", ""},
            { "sub", "" },
            // and other key-values 
        };

        var token = encoder.Encode(payload, new byte[0]);

        return token;
    }

    private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
    {
        var csp = new RSACryptoServiceProvider();
        csp.ImportParameters(rsaParams);

        var algorithm = new RS256Algorithm(csp, csp);
        var serializer = new JsonNetSerializer();
        var urlEncoder = new JwtBase64UrlEncoder();
        var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

        return encoder;
    }

    private static RSAParameters GetRsaParameters(string rsaPrivateKey)
    {
        var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
        using (var ms = new MemoryStream(byteArray))
        {
            using (var sr = new StreamReader(ms))
            {
                // use Bouncy Castle to convert the private key to RSA parameters
                var pemReader = new PemReader(sr);
                var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
                return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
            }
        }
    }

ps: the RSA private key should have the following format:

-----BEGIN RSA PRIVATE KEY-----

{base64 formatted value}

-----END RSA PRIVATE KEY-----

Lindstrom answered 22/5, 2019 at 4:10 Comment(1)
THANK YOU for this!!! I spent many hours over many days looking for something that would do what I needed, and this was the ONLY thing that worked for me in C#.Indiscreet
P
0

I'll list my answer in steps

  1. For this, you'll need to install a package to get a library called Jose-jwt. this stands for JavaScript Object Signing and Encrypting. Install the package from the NuGet package manager, Install-package Jose-jwt
  2. Using OpenSSL, pack your private key in the form of a PKCS12 file (*.p12), you'll set a password for the file in the process.

    openssl pkcs12 -export -nocerts -in ./myKey.key -out my-Key.p12
    
  3. as listed on the Library's readme, you'll need to generate an RSACryptoServiceProvider from that file, like this:

    var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;
    
  4. Use the RSACryptoServiceProvider createdalong with the password set up during the PKCS12 packing to encode your payload like this:

    string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);
    
Puzzle answered 4/12, 2017 at 11:11 Comment(0)
E
-1

I recently had to achieve something similar and came across an error "Invalid Algorithm Specified" when signing my payload, so have solved my specific issue I thought I'd share the code. I think that may be useful to you too.

You can find a full explanatory [ReadME][2], and source code at Karama.Jwt.Public. I happen to be using a different library for generating my JWT, namely JOSE, but I think that this is incidental, and for completeness, there is a project achieving the same end using no third party libraries.

Please let me know how you get on.

Erotic answered 3/4, 2016 at 0:58 Comment(0)
M
-5

I found some purely Javascript based solution, if it is useful to anyone. You can find the JS Libraries here.

It has resolved my requirement.

Minetta answered 27/10, 2014 at 14:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.