Signed and Encrypt from KeyPair string

AccRpa picture AccRpa · Apr 30, 2018 · Viewed 11k times · Source

I'm a newbie in jwt and after read a lot of web pages I've not found examples of how to generate a token (signed and encrypted) with keypairs generated from this website https://mkjwk.org/. I think it can't be very difficult. I think this is the way to generate the signed token (I don't know if it's correct):

        RSACryptoServiceProvider rsaCrypto = new RSACryptoServiceProvider();

        var payload = new Dictionary<string, object>()
        {
            {"jti", "ORIGEN_" + Guid.NewGuid().ToString()},
            {"iat", DateTime.UtcNow},
            {"exp", DateTime.UtcNow.AddYears(1)},
            {"login", "USER" },
            {"password", "PASSWORD" },
            {"origen", "ORIGEN" }
        };

        // Contains both public and private keys to sign
        var headers = new Dictionary<string, object>()
        {
            { "kty", "RSA" },
            { "d", "A7Q8cttv_CSG4CJkX_xlU5lUoeRrCPZpyZx9eVaD7zi-tE7wDPKNmJPRP6uR_LA2YVXMmfY9w8q1_v_MiYxkYnFgZqNZlKdwucSQUlnfX5Tt806qh_323h5NnHrKweL-98_d8R4RuZXCWEQ3X0QDCVfccaLVVqLJy8S5zlx0aAVuBJxLxBHFRO700qdUN-RaMjHULoOnE1KbwmfKPfGlLL0YWPHQ9t-qIBh6OSZsDZh30K4VLF8sRXkGgn81_Byp4hK9tCfG98R6fWUM2_FCQrC9R1hO-KTsLffRzMboWe-2ymZGQfZKO-gtFaQH7_AjdVnQYMyKhSSCGYAAroSZAQ"},
            { "e", "AQAB" },
            { "use", "enc"},
            { "kid", "RPA" },
            { "alg", "RS256" },
            { "n", "qJPwMcHtb7xFGGczn20IiEtrPVehquyT6lxIJa_e4vcZE33uM6myVZWocTZWzTDmrNT3bJghEpLOhrgYatT3QnJIiTM9KAD01kYPc5cP5yo6Wmu0YjivqL3Rj7dUvi2pvl7juwYxt1_8zfdnBN5GpBIYcaY3ulVo_OSL7TOxJrua5IMhilQz6kqta3-Rgz3GSglOs94RHRvorYxMyHPQ6KhwSlh_zLzJQZ-0-AZ4yaMPdVwEaaEJpL-odYmRudX4E0t42dExLf_q1rpRfvTcdFSwfsJ7FmQcOtlc340WUgr4BHJfwrNIE4i-TFqrB4zSQJVKHlBfLeGKiYZQPD7igw" }
        };

        string tokenSigned = JWT.Encode(payload, rsaCrypto, JwsAlgorithm.RS256, headers);

I get a token (I think is signed) but if I put this token in this web site https://jwt.io/, I get an error "Invalid Signature". Also I would like to encrypt this token with the public key. So I do this:

// Another public key to encrypt
headers = new Dictionary<string, object>()
        {
            { "kty", "RSA" },
            { "e", "AQAB" },
            { "use", "enc"},                
            { "alg", "RS256" },
            { "n", "ldMvqNDlz8-ABqEhqjtT0qvjKKbJMQ4J6GEi-7QrY-EUtyjCE7cOriHrYmbjt3o3zXwUTyOp0-twnF5j1HXFwVk7_XNsZz7LUmGNtmnqgB2iw2xhS7LAicN0RRgIbxWRDLOaaZ-49QumX6_r_jLNtIspKiFiuUNf2s0ipeAjWBFquiiqTMBd98z3pS-vC5y0CfzPbTSLSinikrHkIW2uO4FNHWZpoo8npn7vwWtAJjknWhaFi2s9P5kzUk4Mpbdx4DxUJ9ZvUi9SmdvH2vUzwGe0lxyvlw0DAMMWAT9TmsiKzBeXTY6rQ1-2Edn4F9S5kkPNOh1NqJoebz50-Bpl6w" }
        };

string tokenEncrypted = JWT.Encode(payload, tokenSigned , JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM, extraHeaders: headers);

But I always get the error "RsaKeyManagement alg expects key to be of RSACryptoServiceProvider type.". I've already search about this error but I don't have anything clear. Please, anyone can help me? I'm not sure if I'm on the right way.

I use jose-jwt for .net because I've read that the library System.IdentityModel.Tokens.Jwt can't encrypt.

Thank you. Regards.

Answer

AccRpa picture AccRpa · May 11, 2018

In this case I had a jwk and I wanted to sign it with my private key and encrypt it with a customer public key with c# jose-jwt library. This jwk is the same all the time, so I only need to generate it once. To do that, you need to create an RSA object and then use the Encode method of the library to sign and encrypt. A jwk has severals parameters:

JSON Web Key (JWK): A JSON object that represents a cryptographic key. The members of the object represent properties of the key, including its value. These are some of the parameters:

p = 'RSA secret prime'; kty = 'Key Type'; q = 'RSA secret prime'; d = 'RSA secret exponent'; e = 'RSA public exponent'; kid = 'Key ID'; n = 'RSA public modulus'; use = 'Public Key Use'; alg = 'Algorithm'

But in my case I only had some of them: d, e, n, kty, use, kid, alg. The problem is that if you have e and d parameters, you also need p and q, because you can't construct private key with .NET without primes (P and Q).

The solution was to part the problem in two parts:

JAVA part: create a complete jwk with the Nimbus JOSE+JWT JAVA library:

C# parts: Use the previous jwk to create an RSA object in C# with c# jose-jwt library. Like this:

var js = new JavaScriptSerializer();    
// json is the result returned by java
var jwk = js.Deserialize<IDictionary<string, string>>(json);

byte[] p = Base64Url.Decode(jwk["p"]);
byte[] q = Base64Url.Decode(jwk["q"]);
byte[] d = Base64Url.Decode(jwk["d"]);
byte[] e = Base64Url.Decode(jwk["e"]);
byte[] qi = Base64Url.Decode(jwk["qi"]);
byte[] dq = Base64Url.Decode(jwk["dq"]);
byte[] dp = Base64Url.Decode(jwk["dp"]);
byte[] n = Base64Url.Decode(jwk["n"]);

RSA key = RSA.Create();
RSAParameters keyParams = new RSAParameters();
keyParams.P = p;
keyParams.Q = q;
keyParams.D = d;
keyParams.Exponent = e;
keyParams.InverseQ = qi;
keyParams.DP = dp;
keyParams.DQ = dq;
keyParams.Modulus = n;

key.ImportParameters(keyParams);

Once you have an RSA object, you can sign it:

var payload = new Dictionary<string, object>()
{       
    {"user", USER },
    {"password", PASSWORD }            
};

string tokenSigned = JWT.Encode(payload, key, JwsAlgorithm.RS256);

You can find the original solution in the library author web page.

Regards.