Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sign and Encrypt from KeyPair #92

Closed
AccRPA opened this issue Apr 30, 2018 · 22 comments
Closed

Sign and Encrypt from KeyPair #92

AccRPA opened this issue Apr 30, 2018 · 22 comments

Comments

@AccRPA
Copy link

AccRPA commented Apr 30, 2018

Hi!, I'm a newbie in jwt and I would like to generate a signed and encrypted token with private and public key generated from this website https://mkjwk.org/, but I've not found any examples in this repository about this, does this mean that it's not possible to do it with this library?
I had the hope to find examples in the wiki, but it's empty. Also, I've looking for this in internet without any good results. Any help please?

Thanks.
Regards

@dvsekhvalnov
Copy link
Owner

Hi @AccRPA ,

well, there are a lot of examples here: https://github.com/dvsekhvalnov/jose-jwt

And it's definitely possible to sign or encrypt tokens or other arbitrary payload with the library.

What are you looking to exactly?

@AccRPA
Copy link
Author

AccRPA commented Apr 30, 2018

Hi @dvsekhvalnov, I'm a little stuck, because I generate key pairs (public and private) with this page https://mkjwk.org/, and I would like to get a token signed (RS256 ) and encrypted (RSA_OAEP and A128CBC_HS256). But the examples that I found are with p12 certificates:

var privateKey=new X509Certificate2("my-key.p12", "password").GetRSAPrivateKey();

I think that I already have a private key so I don't need to do the code above. If I convert the private key to byte[] and pass it to Encode method I get this error:

'RsaUsingSha alg expects key to be of AsymmetricAlgorithm type.'

And also I've tried to pass an RSACryptoServiceProvider object, but I think it's not correct. This is an example code to illustrate the case:

`// KEY PAIR GENERATED FROM THE WEB TO SIGN
// "{" +
// " "kty": "RSA",\n" +
// " "d": "PRIVATE KEY",\n" +
// " "e": "AQAB",\n" +
// " "use": "enc",\n" +
// " "kid": "RPA",\n" +
// " "alg": "RS256",\n" +
// " "n": "PUBLIC KEY"\n" +
// "}";

string myPrivateKey = "PRIVATE KEY";
byte[] mpkArray = Encoding.UTF8.GetBytes(myPrivateKey);

// PUBLIC KEY GENERATED FROM THE WEB to ENCRYPT
string theirPublicKey = "PUBLIC KEY";
byte[] tpkArray = Encoding.UTF8.GetBytes(theirPublicKey);

var payload = new Dictionary<string, object>()
{
{"jti", "Test_" + Guid.NewGuid().ToString()},
{"iat", DateTime.UtcNow},
{"exp", DateTime.UtcNow.AddYears(1)}
};

var headers = new Dictionary<string, object>()
{
{ "kty", "RSA" },
{ "d", "PRIVATE KEY"},
{ "e", "AQAB" },
{ "use", "enc"},
{ "kid", "RPA" },
{ "alg", "RS256" },
{ "n", "PUBLIC KEY" }
};

RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);

// Am I getting a signed and encrypted token here or I need to do separately?
string token = JWT.Encode(payload, RSA , JwsAlgorithm.RSA_OAEP, JweEncryption.A256GCM, extraHeaders: headers);`
Any help will be welcome!

Thanks.
Regards.

@dvsekhvalnov
Copy link
Owner

Ok, how your public and private key files looks like?

  1. Generally speaking you need to combine them to .p12, because this is what library expects. It's straightforward with openssl:

openssl pkcs12 -export -out my-key.p12 -inkey privateKey.key -in certificate.crt

You can also take a look here: https://github.com/dvsekhvalnov/jose-jwt#if-you-have-only-rsa-private-key

  1. signing - ensures integrity of messages (e.g. nobody changed content). encryption - making sure nobody can read it besides intended parties, it also ensures integrity.

I'm not super sure what is purpose of signing encrypted messages, but if you really need to:

string encrypted = JWT.Encode(payload, key , JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
string encryptedAndThenSigned = JWT.Encode(encrypted, key, JwsAlgorithm.RS256);

@AccRPA
Copy link
Author

AccRPA commented May 2, 2018

Hi dvsekhvalnov!, thank for your answer. That I would like to do is generate a jwt from a jwk. The private and public key are part of a json, I don't have .p12 or physical certificate that is used in the link you mentioned. I've just found this question that it's very similar to my problem, but I don't have very clear the solution.

Thanks a lot.
Regards.

@dvsekhvalnov
Copy link
Owner

Hey @AccRPA ,

you have JWK? can you post sample here? Please strip all sensitive values or better generate sample key.

@AccRPA
Copy link
Author

AccRPA commented May 2, 2018

Hi dvsekhvalnov! Of course I can, these are sample keys:

Configuration:
KeySize: 2048, KeyUse: Encryption, Algorithm: RS256

This is my private and public key:
{
"kty": "RSA",
"d": "qRWqLPOJ6RH9YmrANAyKOi7Rt5SKj0JmeNqKeuxFmXr4jtDwSs2WMcfzUX_ymwJskiwR6iqgeMXdgIBl-TyxVWtp9tXKlRv89oi264KN1-Yi_s9cM1Da1pY8Hp8bOMy4Y9im7ZSkFSHw-HU91Ir3xnXAjB64H7qDi2k9IRELcIz63t5_NtN_BvUpA-aFDhUBAWI6eDXENhEkYGmzoGUIyALa6Ib4rco2i2mIvAEwozwQos1QUXl5ubqrydtIj1pOJOwG-31CZXF2z9p26PKOC6D4iqlI3nTVV4kogrdJyitDss-6MTm27rsPKQqdorIbJYgbfvi3hpCAr0KRMX6-0Q",
"e": "AQAB",
"use": "enc",
"alg": "RS256",
"n": "zjqbW24mX-LCR8xxmBz_MFHZo8iSpOWb-YtgWIikfa9Uwrblqp-KwdTUx8rGFcI5X-AA3kQ_MNclv9Cd1uWVrjNYvunsmqRtAAW0fKj8jrz-kgl1CcbPYyBWsfse4IDBjCp0Hdds9pJSVDb4ymQjNJdAj3WDNzT69EnPomHjwV43K5-Y42xJwz-MjG6z0aTMQwdx0PwpUo7HMIpoZySntZ_CIEPlMjYlbaz_5v74LXOLqgFrK0xiwgXXVkXZZDuIlWMiCmigcift47YS160q5XVlY6wnlo6BcYreXStCT4uFk1QXfLNeVABzsT4Vobfsepo3uE1vhK2dW4RcvFBJ-Q"
}

And this is the customer public key:
{
"kty": "RSA",
"e": "AQAB",
"use": "enc",
"alg": "RS256",
"n": "t72x97BewN7jyRn2xy-bTeUFvBqq4jyKm7xQNlWNWnvuN6uB1ZWSdXD7T01fdtXiWyK8XR0zlF6p-sSyEuxKGdoD4E-bzXTZuhUAuoNKymImHNCcyUJecKCsEAWKCJEiSbPtgUoJ1dYX2CoYO75aeMvGgGhPD6cLWGhVmUSXmbv8ejLLWf_rJzGTe7xORx1zwwLJpqrpJFtWYbPVabyfcGiHJXsYG_HMDz9E28O4SRi-l7PfNIpjGwYBLiHl51swS5ZM6LLobr0r8wCHUMAVxB4KYpjcDS698qDZ1MVsww6550NdqRRKjl-DCqGl8JcJFsyK-Ozkt5UEpl5SuR5QdQ"
}

What I want to do is get a token signed with my private key and encrypted with the customer public key. I've been trying some code but finally I've not achieve nothing. Maybe I'm wrong, some help is welcome!

Thanks.
Regards.

@dvsekhvalnov
Copy link
Owner

dvsekhvalnov commented May 2, 2018

Well, you'd probably have to write it yourself: convert JWK into RSA object.
May be if i have time i'll take a look, but no promises. I'm accepting contributions though :)

Hints:

  1. RSA.Create() then RSA.ImportParameters(..) - to create a keys. See: https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa_methods(v=vs.110).aspx

  2. RSAParameters https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters(v=vs.110).aspx

Here your:
e - exponent
n - modulus
d - private key exponent

Here is sample code created in another thread that you can use as reference: https://github.com/psteniusubi/jose-jwt/blob/master/jose-jwt/jwk/JwkRsa.cs#L54

Make sense?

@AccRPA
Copy link
Author

AccRPA commented May 2, 2018

Hi dvsekhvalnov! thank you so much for your help. Definitely I'll do what you say. I'll post my results.

@AccRPA
Copy link
Author

AccRPA commented May 7, 2018

Hi dvsekhvalnov!, sorry for the delay. I'm trying to follow your steps, and I've needed to convert the e, n and p parameters with Convert.FromBase64String. After that, when I call the JWT.Encode method, I receive the below error:
System.Security.Cryptography.CryptographicException: 'Keyset does not exist'

This is a part of the code:

RSA rsa = RSA.Create();
RSAParameters RSAParams = new RSAParameters
{
	Exponent = Convert.FromBase64String(e),
	Modulus = Convert.FromBase64String(n),
	D = Convert.FromBase64String(d)
};
rsa.ImportParameters(RSAParams);
rsa.KeySize = 2048;         

JWT.Encode(payload, rsa, JwsAlgorithm.RS256, headers);

I'm looking some information and solutions about this.

Thank you.
Regards.

@dvsekhvalnov
Copy link
Owner

Mm, if you remove rsa.KeySize=2048 it still throwing an exception?

@AccRPA
Copy link
Author

AccRPA commented May 7, 2018

Yes. I thought I needed to indicate this parameter.

@dvsekhvalnov
Copy link
Owner

dvsekhvalnov commented May 7, 2018

Ok, not much ideas without trying that.

May be .NET doesn't like that you don't provide DP/DQ/InverseQ params along with D. Is there are way to have a JWK with all additional params?

@AccRPA
Copy link
Author

AccRPA commented May 7, 2018

Well, I'm going to try to generate a jwk with jose-jwt java library, and then, use that jwk to compose the RSAParameters in C# and see if the encode method doesn't throw any exceptions.

Thank you so much for your help.
Regards.

@AccRPA
Copy link
Author

AccRPA commented May 8, 2018

Well, I've not achieved what I said in the above post. I can generate a jwk like this:

{"p":"2XsxRAadoo1hQgqO3JOKqrsgXZbJFSxlw5phcr2-By3rtNgPttti12YZChYzFOHuJmsWKZDjV0mvtmVt63-kVa2MzAvLXHM4mRUC2N7L6NsQUW3MZSZ7RiEZWSZ2qy7Vy66fcMXeN-GpRUlYXubN3muEFwkk3MMOtB1ibdj2Vhs","kty":"RSA","q":"tClNf-W2TAuZKOf7Td0fKbmSWnwQ7B8-00g-XsRiA_22m1uUTYn68bOBz90DcF98JoFZB_5oyCT9A99r5ak7jRAdkkQwnC9VRgpgackxj9SvxFqJJhCgKiEZPAiKiKOx9vnl3OJVEO1dfd0NpSITY6ED8mARLryLnYzuUfyFW28","d":"ORVmv32a4OUxYjxX_gDk4tHWg_hABO9mFfOSTheOI9BCwpAirnI-uj7qX5oF2D3IfwgNaIGJ2J2E19_tR8vaWZqgYc7Fd-iC830nqHvEZHXYEjK-SWfHCBmwlgqvLNYVp-a1N99Q-E8S5jsj7XDLWe9oudnDxpC_qCyjgfjeVrne72XslqDeyLNqVNdrwNVqORUbW_QmcOW_olMfwqBGpfEQce59v4ctlqginUr2ktLVau3u6i4MSe0wg5Ja_JQRiFNFydBE4zTl6nhuKuPWRyUs_wpJ3Cq5OhZzOYSYSYf4A_KgXx28YFgAtC_hBv23yzLrsNJsCPl0K2liDd24JQ","e":"AQAB","kid":"e5c1c652-12c6-4012-9c72-e37fee52122f","qi":"R8PL8elT-3c3iw4JaafZO4A4NiPbsGjlNN8NfKAte4zVifoQ7vmZMSJ2yU4mGIguVhni2c_a8oVMqHOiJPxojmS-QrW6Tyis-O9pyeR3cBs6GwXeOq3Rwri-ppZzL157b6w5ptUqNxEOU0B2ocsM-dfIFc9jx57KbLspZhVB7WI","dp":"a-wwVHub-jCClQ08O8WTyIm30_mhq7oufdS8iv4RsOpez41wruNLt2xY_KJrku9TCQmXI-Vci9JrOe43j-f1mvbWqeModaZP7dd5ZDmbouAixuXfykpAXlrKg20M8oH5YmwzfvkR_1UidNmi2uVaQAfGss-81TiOsV7rm2tRQwU","dq":"d2fYgagSBpy5Si9Wk-i2OsVPhsErxhN_ZDFBhTXLcLG7UYSE6k9FDuTWaJonKVdfxXV9OJsZf21kdCikLnbXjUrdDpA7V4jXj7BY7kP2oUlppU4MEDp8rO969hsDFkTIMtS10IwBrVJk4IKeBJBtIV8aao3ZVYyJTEaXvacMC-k","n":"mQ21NW2g3Yhwb5SGYG1JyB8uwvAcoCb-E5o-YC-NKeMZfYLwubkblNqbLbMJOtMr3EVL1-WhvdKfsPSLNXUVRAN_ecPFSyVjbGkkueq01c4sYvCT7VZQv4tKzuy7N217qJ080yX0KUP0CDuC3twC61K3BkH6aSMOforEvNv8RGuPmpitHjTsvUKMTBg-MguGn4bvQiIztEYNVD9BSac8iJpY3PZtt1R52LLb7xpd5PtT0rHNbN7-1BqGOXGqWvAOxmNEG-lQqHAeOncHvzsDuwDimgDIL3UQJIif4xTejLpnnNQmaSQ1PB7qEijdOBdRtKrhY77F4DQfIUcI83HutQ"}

But when I call to the rsa.ImportParameters(RSAParams) method, it raises an Exception: "An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
The parameter is incorrect."

I don't have any ideas.
Thank you.
Regards.

@dvsekhvalnov
Copy link
Owner

Ok, let me try to play with it myself. I'll try to give it a shot this week.

@dvsekhvalnov
Copy link
Owner

Hey @AccRPA , here is working snippet for me with your latest key:

            string json = @"
{
	""p"": ""2XsxRAadoo1hQgqO3JOKqrsgXZbJFSxlw5phcr2-By3rtNgPttti12YZChYzFOHuJmsWKZDjV0mvtmVt63-kVa2MzAvLXHM4mRUC2N7L6NsQUW3MZSZ7RiEZWSZ2qy7Vy66fcMXeN-GpRUlYXubN3muEFwkk3MMOtB1ibdj2Vhs"",
	""kty"": ""RSA"",
	""q"": ""tClNf-W2TAuZKOf7Td0fKbmSWnwQ7B8-00g-XsRiA_22m1uUTYn68bOBz90DcF98JoFZB_5oyCT9A99r5ak7jRAdkkQwnC9VRgpgackxj9SvxFqJJhCgKiEZPAiKiKOx9vnl3OJVEO1dfd0NpSITY6ED8mARLryLnYzuUfyFW28"",
	""d"": ""ORVmv32a4OUxYjxX_gDk4tHWg_hABO9mFfOSTheOI9BCwpAirnI-uj7qX5oF2D3IfwgNaIGJ2J2E19_tR8vaWZqgYc7Fd-iC830nqHvEZHXYEjK-SWfHCBmwlgqvLNYVp-a1N99Q-E8S5jsj7XDLWe9oudnDxpC_qCyjgfjeVrne72XslqDeyLNqVNdrwNVqORUbW_QmcOW_olMfwqBGpfEQce59v4ctlqginUr2ktLVau3u6i4MSe0wg5Ja_JQRiFNFydBE4zTl6nhuKuPWRyUs_wpJ3Cq5OhZzOYSYSYf4A_KgXx28YFgAtC_hBv23yzLrsNJsCPl0K2liDd24JQ"",
	""e"": ""AQAB"",
	""kid"": ""e5c1c652-12c6-4012-9c72-e37fee52122f"",
	""qi"": ""R8PL8elT-3c3iw4JaafZO4A4NiPbsGjlNN8NfKAte4zVifoQ7vmZMSJ2yU4mGIguVhni2c_a8oVMqHOiJPxojmS-QrW6Tyis-O9pyeR3cBs6GwXeOq3Rwri-ppZzL157b6w5ptUqNxEOU0B2ocsM-dfIFc9jx57KbLspZhVB7WI"",
	""dp"": ""a-wwVHub-jCClQ08O8WTyIm30_mhq7oufdS8iv4RsOpez41wruNLt2xY_KJrku9TCQmXI-Vci9JrOe43j-f1mvbWqeModaZP7dd5ZDmbouAixuXfykpAXlrKg20M8oH5YmwzfvkR_1UidNmi2uVaQAfGss-81TiOsV7rm2tRQwU"",
	""dq"": ""d2fYgagSBpy5Si9Wk-i2OsVPhsErxhN_ZDFBhTXLcLG7UYSE6k9FDuTWaJonKVdfxXV9OJsZf21kdCikLnbXjUrdDpA7V4jXj7BY7kP2oUlppU4MEDp8rO969hsDFkTIMtS10IwBrVJk4IKeBJBtIV8aao3ZVYyJTEaXvacMC-k"",
	""n"": ""mQ21NW2g3Yhwb5SGYG1JyB8uwvAcoCb-E5o-YC-NKeMZfYLwubkblNqbLbMJOtMr3EVL1-WhvdKfsPSLNXUVRAN_ecPFSyVjbGkkueq01c4sYvCT7VZQv4tKzuy7N217qJ080yX0KUP0CDuC3twC61K3BkH6aSMOforEvNv8RGuPmpitHjTsvUKMTBg-MguGn4bvQiIztEYNVD9BSac8iJpY3PZtt1R52LLb7xpd5PtT0rHNbN7-1BqGOXGqWvAOxmNEG-lQqHAeOncHvzsDuwDimgDIL3UQJIif4xTejLpnnNQmaSQ1PB7qEijdOBdRtKrhY77F4DQfIUcI83HutQ""
}";

            var js = new JavaScriptSerializer();
            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);

            string token = JWT.Encode("Hello JWK", key, JwsAlgorithm.RS256);

Unfortunately i have to say you can't construct private key with .NET without primes (P and Q). So minimal set of JWK fields to do it would be: e, n, p, q and them you can use RsaKey.New(...) helper.

In theory P and Q can be recovered from D, see latest answer in SO thread: https://stackoverflow.com/questions/2921406/calculate-primes-p-and-q-from-private-exponent-d-public-exponent-e-and-the

but i would not recommend it.

@dvsekhvalnov
Copy link
Owner

Okay, correction, if you only getting JWK once (like registering) and not dealing with (N,E,D) keys runtime. You can recover P and Q given procedure above and then just keep full JWK version of RSA key. Or better convert it to .p12.

@AccRPA
Copy link
Author

AccRPA commented May 10, 2018

Hi dvsekhvalnov!, Your solution works very fine. In my case, I only need to get the Jwk once so I work with the same jwk all the time.

Thank you so much for your help!
Regards.

@dvsekhvalnov
Copy link
Owner

Feel free to close if your issue is resolved :)

@AccRPA
Copy link
Author

AccRPA commented May 11, 2018

Hi dvsekhvalnov, if I'm right, the token generated in this line:

string token = JWT.Encode("Hello JWK", key, JwsAlgorithm.RS256);

is a signed token. Is is correct to do the code below to encrypt this signed token with a public key of the customer?

var js = new JavaScriptSerializer();
var jwk = js.Deserialize<IDictionary<string, string>>(PUBLIC_KEY);
byte[] n = Base64Url.Decode(jwk["n"]);
byte[] e = Base64Url.Decode(jwk["e"]);

RSA key = RSA.Create();
RSAParameters keyParams = new RSAParameters();
keyParams.Modulus = n;
keyParams.Exponent = e;
key.ImportParameters(keyParams);
            
string tokenEncrypted = JWT.Encode(tokenSigned, key, JweAlgorithm.RSA_OAEP, JweEncryption.A128CBC_HS256);

And also, is there any way to change the length of the token? And do you know why if I put the signedToken or encryptedToken generated in this page always get "Invalid Signature"?

Thanks.
Regards.

@dvsekhvalnov
Copy link
Owner

  1. The code looks good to me (did you run it, is it working? :))
  2. What do you mean by "change length of the token"?
  3. You probably put wrong key to jwt.io.

@AccRPA
Copy link
Author

AccRPA commented May 14, 2018

Hi dvsekhvalnov, yes, the code worked fine. My question is that in java library the length of the token is always the same (1212) but in this case I get a longer token. I've realized that if I change the expiration date in the payload (among other parameters), I get a token with different length.
By the way, I also put this issue in stackoverflow and I used your code to write a complete answer with a reference to this post (I hope you don't mind. If it does, let me know, please). No more questions for now, my issue is resolved and I'm going to close it.

Thanks so much for your help.
Regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants