简体   繁体   中英

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

I have my own private key string, ie

-----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.

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.

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); 

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-----

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM