Search code examples
c#cryptographyrsa

Unable to generate public key from given mod and exp. Throws "Bad Data"


I wanted to know and understand the process to generate the JWE. I have given below details:

string mod = "2737"; // this is a 618 char long string constructed only with digits.

string exp = "65537";

string kid = "APIKEY.XX.665_Priv";

string keyEncAlgo = "RSA-OAEP";

string contentEncAlgo = "A256GCM";

And a payload in json format.

As with my limited knowledge in this field I proceeded with creating a public key using RSACryptoServiceProvider. And planned to use Jose.Jwt library.

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAPaeameters rsaKeyInfo = new RSAPaeameters();
rsaKeyInfo.Modulus = Encode.ASCII.GetBytes(mod);
rsaKeyInfo.Exponent = Encode.ASCII.GetBytes(exp);
rsa.ImportParameters(rsaKeyInfo);

The last line in the above code rsa.ImportParameters(rsaKeyInfo) throws exception as bad data.

Although the same mod and exp being used in a Java app which eventually gets everything right. I am converting that Java code to C#. What I am doing here wrong. Or I have understood the process wrong.


Solution

  • Modulus (assuming that 2737 is merely the beginning) and exponent appear to be decimal representations (as string).

    Under .NET5+ (and .NET Core 2.1+) these can be imported as follows:

    using System.Numerics;
    using System.Security.Cryptography;
    ...
    string mod = "2737...";
    string exp = "65537";
    var rsaPublic = new RSACryptoServiceProvider();
    rsaPublic.ImportParameters(new RSAParameters
    {
        Modulus = BigInteger.Parse(mod).ToByteArray(true, true),
        Exponent = BigInteger.Parse(exp).ToByteArray(true, true)
    });
    

    Note that the byte arrays are unsigned (1st true) and with big endian byte order (2nd true).


    An example of JWE with jose-jwt is:

    using Jose;
    using System.Collections.Generic;
    ...
    IDictionary<string, object> addHeaders = new Dictionary<string, object>()
    {
        { "kid", "APIKEY.XX.665_Priv" },
    };
    
    string payload = "some string";
    string token = JWT.Encode(payload, rsaPublic, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM, null, addHeaders);
    

    with the following header (if Base64 decoded):

    {
        "alg":"RSA-OAEP",
        "enc":"A256GCM",
        "kid":"APIKEY.XX.665_Priv"
    }