Coder Social home page Coder Social logo

jwt_decode returns 22 about libjwt HOT 81 CLOSED

kvijay1918 avatar kvijay1918 commented on August 18, 2024
jwt_decode returns 22

from libjwt.

Comments (81)

benmcollins avatar benmcollins commented on August 18, 2024

There is no size limit. I’d need to see an example of the JWT causing the problem. Along with the key.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I have debugged the issue. The issue is because of algorithm type in the header. I am using PS384 which is not supported in this source.

jwt.c

jwt_alg_t jwt_str_alg(const char *alg)
{
if (alg == NULL)
return JWT_ALG_INVAL;

if (!strcmp(alg, "none"))
	return JWT_ALG_NONE;
else if (!strcmp(alg, "HS256"))
	return JWT_ALG_HS256;
else if (!strcmp(alg, "HS384"))
	return JWT_ALG_HS384;
else if (!strcmp(alg, "HS512"))
	return JWT_ALG_HS512;
else if (!strcmp(alg, "RS256"))
	return JWT_ALG_RS256;
else if (!strcmp(alg, "RS384"))
	return JWT_ALG_RS384;
else if (!strcmp(alg, "RS512"))
	return JWT_ALG_RS512;
else if (!strcmp(alg, "ES256"))
	return JWT_ALG_ES256;
else if (!strcmp(alg, "ES384"))
	return JWT_ALG_ES384;
else if (!strcmp(alg, "ES512"))
	return JWT_ALG_ES512;

return JWT_ALG_INVAL;

}

any solutions for this? or will it be supported?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

At the moment, no, it's not supported. You'd either need to use a different algorithm, or look at helping to implement those algorithms in the source code.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I found this library https://github.com/xmidt-org/cjwt

and it has some samples also. here is a sample for RS256 algorithm,
https://github.com/xmidt-org/cjwt/blob/main/examples/basic/rs_example.c

it works fine as it is. and it is mentioned that PS is also similar to RS. So I tried my JWT token by replacing in the input. but hitting into below error from function EVP_DigestVerifyFinal(md_ctx, in->sig.data, in->sig.len) in jws_evp_openssl.c

Error code : 67625094 Error str : error:0407E086:rsa routines:RSA_verify_PKCS1_PSS_mgf1:last octet invalid

I hope you can help me on this. If we can fix this, we can add this source also to your repository.

update me your thoughts.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

The fix will need to be done in the openssl and gnutls code in LibJWT. Looks like cjwt only changes the expected padding. I think I can use that to change up LibJWT to support the PS algos. I'm out of town for work right now. I might be able to get into this over the weekend.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

@benmcollins any further update?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Not yet. It was a busy weekend and now the work week has started. I'll try to find some free time.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Sure

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I just pushed some initial code for OpenSSL. It's untested, but should work. I still need to write some test cases and add GnuTLS support before I can release it.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I tried with your initial changes. I have given my JWT token and its key from JWKS as input. Now also getting error code 22. I debugged this and came to know that, in file jwt-openssl.c,

pkey = PEM_read_bio_PUBKEY(bufkey, NULL, NULL, NULL);
if (pkey == NULL)
	VERIFY_ERROR(EINVAL);

this function unable to read the public key it seems. I have tried two other function as the key type is RSA public key,

  1. PEM_read_bio_RSA_PUBKEY
  2. PEM_read_bio_RSAPublicKey

These also return same error.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Your key needs to be in PEM format.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

What do you mean by key here?

ret = jwt_decode(&jwt, jwt_token, keyid, strlen(keyid));

This highlighted key id ? or the public key which will be generated inside jwt_decode? if you talk about public key, then that is not being given as input to this jwt_decode function. Isn't it?

I think that is the one pkey = PEM_read_bio_PUBKEY(bufkey, NULL, NULL, NULL); function tries to load.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

jwt_decode() does not generate a public key. It converts a public key in PEM format (passed as the third value) to an internal resource used by OpenSSL.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

This is an example of a 2048 bit RSA Public Key in PEM format:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh
uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+
iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM
ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g
6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe
K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t
BwIDAQAB
-----END PUBLIC KEY-----

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Oh Sorry. I got it. In token header, actually a key id would be there. I have provided that key as input by mistake.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Now able to load the public key successfully. But getting error in EVP_DigestVerifyFinal() function. I tried printing the error code as well as the error string.

/* Now check the sig for validity. */
if (EVP_DigestVerifyFinal(mdctx, sig, slen) != 1){
	char *buf = NULL, *str = NULL;
	unsigned long errcode = ERR_get_error();
	printf("Error code : %lu\n", errcode) ;
	str = ERR_error_string(errcode, buf);
	printf("Error str : %s\n", str);
	printf("Error buf : %s\n", buf); 
	VERIFY_ERROR(EINVAL);
}

Error code : 101224597
Error str : error:06089095:digital envelope routines:EVP_PKEY_CTX_ctrl:no operation set

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Would you be able to provide the code, JWT, and key you are using so I can replicate this locally?

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Sorry to say this. I can't expose my token here. I can provide my public key. Will that be ok?

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I feel like the issue is related to the contexts that you create.

mdctx = EVP_MD_CTX_create();
if (mdctx == NULL)
	VERIFY_ERROR(ENOMEM);

pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (pkey_ctx == NULL)
	VERIFY_ERROR(ENOMEM);

and their usage. Am not sure. Just suspecting it.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Can you generate an simple token without any sensitive information and provide the public key for it?

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Token:
eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.G59Ud9dMn2vBP9FCXfSbzpvYcydgTzfXioBR0BTaJTPuNaaYX-EKf-DRnKNSRb5uK553olsmEZ66 t9C0RXT5jsMqlhqSLOJFvtSNeV5uALRmgSIdIx6ApI2Q8LppDYEbzHGEAjkotf9L28SJU43B0wjE PMfWBOZdQE6lEFX9jBPuI7o6va4Vo1Xt9thBIbAkRhq44pEqJ9ziySR0aW_jdBE1tC_05sfsYCIz DAnV2CbonYzFKHB0AHnNHGchazSsjmRjZ3-kelIOMY31ndJTqRtNWivjY-txThZkxTg0BnC_ekOi Frep2Bb3eTfHumxZIMZ8hzIWec7safFCP0ppFg

Public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtP2IRnsaYbxOylgebl0O
FitQz78d7L2Ni8XJ4Nz9t4wjJ4Ved5FS05QrGdqPtj1uVs3bQTVahs4OV5wmsfid
D50YzPBqSNvIOM+FcC0wWOqf3ssNCto8WWRM4G8OONoDWiq5wmEFasjODv20jplC
dwrCbKTa9RrrGpbgP68KaQGb/E2HlyCuKIZIVWnV9Z1HKOdStk7uGVqeBmGpdI+O
ebP4/m/qOf1MJicKh+Czr+evqq3ww/NM6hEudMqFTtAa+DXuc5eVc4Kc3wihF3Vm
it9jRbIKogr6JPWznom4g4cNxnIERigquwJoHgR3paEyP/mrESAkyUVva76F2Mhe
SQIDAQAB
-----END PUBLIC KEY-----

You can try this. and below are the commands that I used to generate the above.

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

HEADER='{"alg":"PS384","typ":"JWT"}'
PAYLOAD='{"sub":"1234567890","name":"John Doe","iat":1516239022}'
BASE64_HEADER=$(echo -n $HEADER | base64 | tr -d '=' | tr '/+' '-')
BASE64_PAYLOAD=$(echo -n $PAYLOAD | base64 | tr -d '=' | tr '/+' '
-')

SIGNATURE=$(echo -n "$BASE64_HEADER.$BASE64_PAYLOAD" | openssl dgst -sha384 -sign private_key.pem | base64 | tr -d '=' | tr '/+' '_-')

JWT_TOKEN="$BASE64_HEADER.$BASE64_PAYLOAD.$SIGNATURE"

just do echo $JWT_TOKEN to get the token.

I tried verifying this signature in jwt.io website. But couldn't.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I have cloned your source locally and added to my sample app. Below is the sample app code.

#include <stdio.h>
#include <stdlib.h>
#include "jwt.h"
#include <string.h>

int main()
{
    jwt_t *jwt = NULL;
    const char *name=NULL;
    int ret = 0, key_id = 0;
    const char* jwt_token = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.G59Ud9dMn2vBP9FCXfSbzpvYcydgTzfXioBR0BTaJTPuNaaYX-EKf-DRnKNSRb5uK553olsmEZ66 t9C0RXT5jsMqlhqSLOJFvtSNeV5uALRmgSIdIx6ApI2Q8LppDYEbzHGEAjkotf9L28SJU43B0wjE PMfWBOZdQE6lEFX9jBPuI7o6va4Vo1Xt9thBIbAkRhq44pEqJ9ziySR0aW_jdBE1tC_05sfsYCIz DAnV2CbonYzFKHB0AHnNHGchazSsjmRjZ3-kelIOMY31ndJTqRtNWivjY-txThZkxTg0BnC_ekOi Frep2Bb3eTfHumxZIMZ8hzIWec7safFCP0ppFg";
    const char* keyid =        
        "-----BEGIN PUBLIC KEY-----\n"
		"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtP2IRnsaYbxOylgebl0O\n"
		"FitQz78d7L2Ni8XJ4Nz9t4wjJ4Ved5FS05QrGdqPtj1uVs3bQTVahs4OV5wmsfid\n"
		"D50YzPBqSNvIOM+FcC0wWOqf3ssNCto8WWRM4G8OONoDWiq5wmEFasjODv20jplC\n"
		"dwrCbKTa9RrrGpbgP68KaQGb/E2HlyCuKIZIVWnV9Z1HKOdStk7uGVqeBmGpdI+O\n"
		"ebP4/m/qOf1MJicKh+Czr+evqq3ww/NM6hEudMqFTtAa+DXuc5eVc4Kc3wihF3Vm\n"
		"it9jRbIKogr6JPWznom4g4cNxnIERigquwJoHgR3paEyP/mrESAkyUVva76F2Mhe\n"
		"SQIDAQAB\n"
		"-----END PUBLIC KEY-----";

    ret = jwt_decode(&jwt, jwt_token, keyid, strlen(keyid));
    printf("ret : %d\n", ret);
    if (ret != 0 || jwt == NULL) 
    {
        return 1;
    }
    
    name = jwt_get_grant(jwt, "name");
    if(name == NULL)
    {
        printf("\nName is null\n");
        return 1;
    }
    printf("Name : %s\n", name);

    return 0;
}

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

one more question which could not directly be related this one. Do you know about JWKS ? how can we generate RSA public key from exponent (e) and modulus (n) fields of JWKS json. basically I have to generate the public key in this way in my case. If you have any idea, please share.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I have fixed the issue now. Now all the verification related openssl functions are passing. But I have few question with you to clarify.

  1. In jwt-openssl.c file, specifically in jwt_verify_sha_pem( ) in function, there is a switch case which checks for the Algorithm type. But at the end of the case, you are trying to read the Public key by using PEM_read_bio_PUBKEY( ) which returns EVP_PKEY*. Is this implementation correct ? If the key of RSA type coming as input, will this function work properly ? there you suppose to use either,
    PEM_read_bio_RSA_PUBKEY( ) or
    PEM_read_bio_RSAPublicKey( )

  2. After this key read, you are trying checking for Key type by using EVP_PKEY_id ( ) which accepts only EVP_PKEY* type as input parameter. So obviously when an RSA key comes in, this will not work anymore.

As of now, I have modified your source as per RSA key. I seperated both RSA and ECC based on padding variable. I give you the code as well. Please have a look and provide your final release.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Please find this Pull request. Review the changes as soon as possible.

#177

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Any further updates? @benmcollins

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Not at the moment. Work has ramped up a little. I’ll find some time to review this week. On why used certain functions, those were in the OpenSSL examples I found and they obviously worked for normal RSA as can be proven through the test cases.

I’ll see what the changes incorporate and make sure it still works with OpenSSL v1.1 as many people still need that compatibility. I may also need to ifdef some of the code for GnuTLS until that code catches up.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Have been waiting for your update. @benmcollins

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I apologize. Full time job has taken precedent this past week. I skimmed over your code. It looks good but I need to do some cleanup. I think I’ll have time to integrate it on Sunday.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Is that done @benmcollins ?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Not yet. I hope to get to it soon.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

hi @benmcollins, please try to get this done by weekend if possible. We have been waiting for this for long time.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

As I'm sure you're aware, I do this project is in my spare time. I have a full-time job and a family. I'll get to it when I have the time. It could be Sunday evening if everything plays out well.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I'm working on this now.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

really sorry for pushing you. I am not aware of this.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I made some changes to the head branch. I noticed they we really weren't using pkey_ctx in association with the original pkey, and the padding wasn't being set for PSS. Can you try the head branch with your token?

I'm not dismissing your code, just wanted to fix mine before merging things.

Also, yes, a test case would help this a lot. Thanks.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I have verified with your latest changes. There is an error in the code, in jwt-openssl.c, in jwt_verify_sha_pem ( ) function, the below line,
if (padding > 0 && (0 < EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding)))

should be,
if (padding < 0 || (0 > EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding)))

I corrected this line by referring my PR (#177). Now the code works fine for my token.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Ok, I left the padding > 0 because that's what I actually want. We don't want to run the command if padding hasn't been set. I did reverse the comparison in the second part, though.

A test case would really make this easier to validate on my end.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Ok, I've added a test case. The generated token validates on JWT.io

You may have an issue. Considering your key worked as the code was, it means you are using a regular RSA key. You need to generate an rsa-pss key.

I did it with the following OpenSSL commands:

openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:2048 -out rsa-pss_key_2048.pem
openssl rsa -in rsa-pss_key_2048.pem -pubout -out rsa-pss_key_2048-pub.pem

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I've tested this and got it working with GnuTLS now.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Ok I will verify from my end. Have you released your source? If I just do, apt install libjwt-dev, will that be fine enough?

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Ok, I've added a test case. The generated token validates on JWT.io

You may have an issue. Considering your key worked as the code was, it means you are using a regular RSA key. You need to generate an rsa-pss key.

I did it with the following OpenSSL commands:

openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:2048 -out rsa-pss_key_2048.pem
openssl rsa -in rsa-pss_key_2048.pem -pubout -out rsa-pss_key_2048-pub.pem

and yes. My token is failing for my public key. Is there any specific reason for this change you made? If possible, please let the code as it was earlier.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I want to make sure it works for someone besides me first. You’ll have to build from the head branch of the repo.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

And the code as it is is correct. Your usage of a normal RSA key is incorrect. It would likely even fail on JWT.io. You need to use the correct RSA-PSS key type.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I've expanded the test cases for code coverage, and tested this through valgrind on OpenSSL 1.1/3.1 as well as GnuTLS 3.8, on both macOS and Linux (Ubuntu 18.04/x64). Everything is passing.

I'll do a release soon.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

And the code as it is is correct. Your usage of a normal RSA key is incorrect. It would likely even fail on JWT.io. You need to use the correct RSA-PSS key type.

I have taken below token and its public key from your tests,

token:

static const char jwt_ps384_2048[] = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.ey"
"JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi"
"I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.HGI3jmvkMT"
"aot4JM3ElUuEp_ufbG2os116tJdiJsZDM310N3q46vTMAax2QBI3qENEJU_p6qd30i-l"
"f8imM1-D-PiPjMHxvkN5sOkt35MqEwT8I2Xo3ikn8TSBJKPLM4uBBgO49wG2_0tYTfp0"
"GPCinBpkSxttH7T3PmK0gqRXde-5TI99XxUbCAtpS9tSAG4RCpQW4XuhA_Hn4WM8pbGZ"
"vrCvSXWg0ms8fibdDzehuL5AGwJdrabGsPJFU7F2ItrfCaQcVsT6aTSLfFPCv91RK1tl"
"PlUkpxVg6UdVNwzG44o6UgnCil1jLXbcJmIozsJ_tMQHMg2R6YCslah1MrNA";

from libjwt/tests/jwt_rsa_pss.c

and public key from /libjwt/tests/keys/rsa-pss_key_2048-pub.pem

This is not actually working in jwt.io and throws invalid signature.

and one more point is, my token along with its public key is getting verified in jwt.io

Can you share a token and its public key here which verifies in jwt.io as well as in your code.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

The PS384 and PS512 do not, correct. I need to fix that.

However, the PS256 token does in fact verify on JWT.io

Screenshot 2023-07-12 at 7 00 17 AM

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Oh..! but that is also invalid signature it shows for me. (PS256 it is)

jwt failure

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

up to Fix logic when check return value of rsa_set_padding this commit. My token works fine which is PS384.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Ok, I've found the current code base works when compiled with GnuTLS instead of OpenSSL.

Until I figure out why the OpenSSL doesn't work, you can compile against GnuTLS.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I get the feeling that when I correctly set the key type to RSA-PSS, we no longer need to set the padding. Let me test that.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

up to Fix logic when check return value of rsa_set_padding this commit. My token works fine which is PS384.

Yes, this works for you because you're still using the wrong RSA key type. Fix your key. Generate an rsa-pss key and use that. Please stop using an incorrect key type to test the code. It won't provide us anything useful.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

At this point, please recompile your LibJWT against GnuTLS. Instructions are in the README.md. Also, please regenerate your key so that it is an rsa-pss key.

If you don't want to do that, then you must change libjwt/jwt-gnutls.c so it uses the RSA and not RSA_PSS alg types. This would be invalid usage, but it would serve your purpose.

I would not suggest using the OpenSSL RSA-PSS support as it is. The test cases show that it is validating improper tokens, and you don't want that.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

I am not directly generating rsa key pair by using openssl. Actually, the public key is getting generated in C code from exponent(e) and modulus(n) values of a jwks data and it is in simple RSA format, Not in RSA_PSS format. I have to look for this conversion from RSA to RSA_PSS format in C.

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Since you're creating the key from a JWK, you should be able to create the key as RSA_PSS using the values contained in it. Can you show me the C code that you use to do this? Maybe I can help with that.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

This is my C code

BIGNUM* bignum_base64_decode(const char* base64bignum) {
    BIGNUM* bn = NULL;

    size_t base64_input_length = strlen(base64bignum);
    size_t output_length = (base64_input_length / 4) * 3; // Estimate the output length
    unsigned char* output = (unsigned char *)calloc(1, (output_length + 1) * sizeof(char));

    int result = base64_decode(base64bignum, base64_input_length, output, &output_length);
    if (result == 0) {
        bn = BN_bin2bn(output, output_length, NULL);
    }
    free(output);
    return bn;
}

int generate_pubkey_from_exponent_and_modulus(char* exponent, char* modulus, EVP_PKEY** pubkey){
   
   BIGNUM *n = bignum_base64_decode(modulus);
   BIGNUM *e = bignum_base64_decode(exponent);

   if (!e || !n) {
    ERROR("Error: Invalid encoding for public exponent or modulus\n");
    return -1;
   }

   if (e && n) {
       EVP_PKEY* pRsaKey = EVP_PKEY_new();
       RSA* rsa = RSA_new();

    if (!RSA_set0_key(rsa, n, e, NULL)) {
        ERROR("Error: Failed to set RSA key components");
        BN_free(n);
        BN_free(e);
        RSA_free(rsa);
        return -1;
    }
       EVP_PKEY_assign_RSA(pRsaKey, rsa);
       *pubkey = pRsaKey;
       return 0;
   } else {
       if (n) BN_free(n);
       if (e) BN_free(e);
       return -1;
   }
}

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I think something like this would work:

EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL);

OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n);
OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e);
OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(param_bld);

EVP_PKEY_fromdata_init(ctx);
EVP_PKEY *pkey = NULL;
EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params);
EVP_PKEY_CTX_free(ctx);

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

looks like OSSL_PARAM_BLD these kind of code is present only in openssl 3.x
but my requirement is in 1.1.x

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Maybe:

EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL);
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_keygen(ctx, &pRsaKey);
EVP_PKEY_assign_RSA(pRsaKey, rsa);

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

this is also generating same RSA key which my code generates already. Look at the below piece:

int generate_pubkey_from_exponent_and_modulus(char* exponent, char* modulus, EVP_PKEY** pubkey) {
    BIGNUM *n = bignum_base64_decode(modulus);
    BIGNUM *e = bignum_base64_decode(exponent);

    if (!e || !n) {
        printf("Error: Invalid encoding for public exponent or modulus\n");
        return -1;
    }

    if (e && n) {
        EVP_PKEY* pRsaKey = EVP_PKEY_new();
        RSA* rsa = RSA_new();
        EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL);

        EVP_PKEY_keygen_init(ctx);
        EVP_PKEY_keygen(ctx, &pRsaKey);
        if (!RSA_set0_key(rsa, n, e, NULL)) {
            printf("\nError: Failed to set RSA key components");
            BN_free(n);
            BN_free(e);
            RSA_free(rsa);
            return 1;
        }
        EVP_PKEY_assign_RSA(pRsaKey, rsa);
        *pubkey = pRsaKey;
        return 0;
    }
    else {
        if (n) BN_free(n);
        if (e) BN_free(e);
        return -1;
    }
}

This complete piece generates the same key as I have already. And if I remove

 if (!RSA_set0_key(rsa, n, e, NULL)) {
            printf("\nError: Failed to set RSA key components");
            BN_free(n);
            BN_free(e);
            RSA_free(rsa);
            return 1;
  }

this piece of code, the output becomes,

-----BEGIN PUBLIC KEY-----
MBQwDQYJKoZIhvcNAQEBBQADAwAwAA==
-----END PUBLIC KEY-----

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Can you add this line after assigning the RSA key:

printf("PKEY ID: %d\n", EVP_PKEY_id(pRsaKey));

And show me what it outputs? The difference should be the key type (RSA_PSS instead of RSA).

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

And, May I know why does RSA_PSS type of key mandatory for PS384?

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Can you add this line after assigning the RSA key:

printf("PKEY ID: %d\n", EVP_PKEY_id(pRsaKey));

And show me what it outputs? The difference should be the key type (RSA_PSS instead of RSA).

PKEY ID: 6

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

By definition the PS algorithms are meant to use RSASSA-PSS keys. You can comment out this check in jwt_verify_sha_pem():

if (pkey_type != type)
            VERIFY_ERROR(EINVAL);

But as I stated earlier, this would be against the specs.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

yeah. I have tried commenting out this check already. It is working. But it expects key type id should be 912. Isn't it? How should I proceed now?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

You might also try adding:

EVP_PKEY_set_alias_type(pRsaKey, EVP_PKEY_RSA_PSS);

After assigning the RSA key to the pRsaKey.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

didnt help.

        EVP_PKEY_assign_RSA(pRsaKey, rsa);
        printf("PKEY ID: %d\n", EVP_PKEY_id(pRsaKey));
        EVP_PKEY_set_alias_type(pRsaKey, EVP_PKEY_RSA_PSS);
        printf("PKEY ID: %d\n", EVP_PKEY_id(pRsaKey));

PKEY ID: 6
PKEY ID: 6

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Seems like with your restrictions on the PKEY creation and OpenSSL 1.1 usage, you'll have to continue using the workaround to LibJWT to disregard the type check in the validation code.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

We are trying to fix it. Will update you on this.

and one more is, regarding the token creation and verifying the same in JWT.io site. I have found few things, in your examples, there is a file called main-gen.c which generates token, isn't it? I tried generating a token which is of RS256 type and took its relevant public key also from your tests folder. This token is getting verified in jwt.io.

In the same way, I just modified the algorithm type to PS256/PS384 and so, generated token is not proper which misses signature in it. Getting below output:

jwtgen: privkey private_key.pem algorithm (null)
priv key loaded private_key.pem (1703)!

{
    "alg": "none"
}
.
{
    "iat": 1689314617
}
jwt algo (null)!
eyJhbGciOiJub25lIn0.eyJpYXQiOjE2ODkzMTQ2MTd9.

and I am using below set of commands to create a JWT token which I shared already in one of the above comments,

To create a JWT token with OpenSSL, you can follow these steps:

Step 1: Generate an RSA key pair with PS384 algorithm:

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

Step 2: Create the JWT header and payload as JSON:

HEADER='{"alg":"PS384","typ":"JWT"}'
PAYLOAD='{"sub":"1234567890","name":"John Doe","iat":1516239022}'

Step 3: Base64-encode the header and payload:

BASE64_HEADER=$(echo -n $HEADER | base64 | tr -d '=' | tr '/+' '_-')
BASE64_PAYLOAD=$(echo -n $PAYLOAD | base64 | tr -d '=' | tr '/+' '_-')

Step 4: Create the JWT token by signing the base64-encoded header and payload using the private key:

SIGNATURE=$(echo -n "$BASE64_HEADER.$BASE64_PAYLOAD" | openssl dgst -sha384 -sign private_key.pem | base64 | tr -d '=' | tr '/+' '_-')
JWT_TOKEN="$BASE64_HEADER.$BASE64_PAYLOAD.$SIGNATURE"

echo $JWT_TOKEN

Token that is produced by these steps, is not getting verified on JWT.io
but decoding is fine.

Any idea on this.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

EVP_PKEY_set_type(pRsaKey, EVP_PKEY_RSA_PSS);
this function sets the key type properly. But after done this, looks like pRsaKey gets corrupted. Not able to do any further operation. I tried writing this key to a file like,

       EVP_PKEY_assign_RSA(pRsaKey, rsa);
        EVP_PKEY_set_type(pRsaKey, EVP_PKEY_RSA_PSS);

        FILE* fp = fopen("pubnew.key", "w");
            if (!fp) {
                printf("Failed to open file for writing");
                return 0;
         }
          ret = PEM_write_PUBKEY(fp, pRsaKey);

This leads to segmentation fault.

Then instead of using pRsaKey directly, I have created another object and copied it contents and using it like,

       EVP_PKEY_assign_RSA(pRsaKey, rsa);
        EVP_PKEY_set_type(pRsaKey, EVP_PKEY_RSA_PSS);

       
        EVP_PKEY *pubkey = EVP_PKEY_new();
        EVP_PKEY_copy_parameters(*pubkey, pRsaKey);

        FILE* fp = fopen("pubnew.key", "w");
        if (!fp) {
                printf("Failed to open file for writing");
                return 0;
         }
         ret = PEM_write_PUBKEY(fp, pubkey );

         fclose(fp);

Same segmentation fault when writing. Any idea?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I explained before that the current code generates bad signatures for the PS algos unless you use GnuTLS instead of OpenSSL. I’m working on it.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

@benmcollins I have fixed issue in creating RSA_PSS key. now able to verify the token successfully. Just a simple change. Replaced
EVP_PKEY_assign_RSA(pRsaKey, rsa); by
EVP_PKEY_assign(pRsaKey, EVP_PKEY_RSA_PSS, rsa); in key generation.

Thanks for all the support. When can I expect the release?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Glad you were able to get this working.

As soon as I fix RSA_PSS signing with OpenSSL, I'll release a new version.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Hi ben, when can we expect the release?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I should be able to finish things up this weekend.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

thanks for your update.

from libjwt.

kvijay1918 avatar kvijay1918 commented on August 18, 2024

Any updates ben?

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

Things are moving along, but took longer than expected. Still working on it.

from libjwt.

eschmidbauer avatar eschmidbauer commented on August 18, 2024

thanks for the update. We are packaging code for debian and this code is causing test to fail in the libjwt-gnutls-dev package

from libjwt.

benmcollins avatar benmcollins commented on August 18, 2024

I strongly suggest backing down to 1.15.3. The 1.16.0 release was withdrawn and is not official.

from libjwt.

eschmidbauer avatar eschmidbauer commented on August 18, 2024

I am not the package maintainer, but I will forward the suggestion along. Thanks.

from libjwt.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.