I am quite new to openssl interface and base64 decoding rules as general and trying to verify JWT signed with private key (RS256 alg) using the solution: Segmentation Fault while verifying JWT token using a public key through openSSL in C++ But my sample JWT is failing to verify.
I only do not have the base64 url decode function, so I manually executed the steps as I understood it - before calling my base64 decode function I replaced '-' -> '+' and '_' -> '/'. To be able to get the proper signature length I also had to manually add the padding ('='). Is there some rule for base64 url decoding that I am missing or is the approach with first replacing the symbols wrong in any way?
I tried first to use RSA_verify, but the error is the same.
The same token and key are validated using python and online JWT validation so they should be ok. I printed the signature after the b64 decoding in hex on the gdb and after the urlsafe_b64decode on the python and the values were the same (except some symbols at the end in the python version) which even more made be believe that the replacing strategy should be working.
// This is the original token
static constexpr const char* buffer_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ9.ZCXtI2nN-d0Cn5dgb3K9JMI41nrEaK_AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh-jDc-Kuq53naOtjkItMcR_vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW_q-9mEu51zWg1HDr1-rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC-kdMZ_GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s-A0iEv_MwdlS6LpJBKU9-d94i1P9Lsqzlg7b_0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX-EU4A5eh2RlDdEvG1YF_zcMARpP1bFV86WTSOuQ";
// This is token with replaced symbols that I am testing before writing the method for url decode
static constexpr const char* buffer_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ9.ZCXtI2nN+d0Cn5dgb3K9JMI41nrEaK/AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh+jDc+Kuq53naOtjkItMcR/vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW/q+9mEu51zWg1HDr1+rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC+kdMZ/GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s+A0iEv/MwdlS6LpJBKU9+d94i1P9Lsqzlg7b/0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX+EU4A5eh2RlDdEvG1YF/zcMARpP1bFV86WTSOuQ==";
// this is how I create the RSA from a key, hopefully successfully because a key is returned with no error
RSA* create_public_rsa(const unsigned char* p_key)
{
BIO *keybio = BIO_new_mem_buf(p_key, -1); // -1: assume string is null terminated
if (!keybio)
{
return nullptr;
}
RSA* l_res = nullptr;
l_res = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
BIO_free(keybio);
return l_res;
}
bool RSAVerifySignature(RSA* rsa, std::string const& token)
{
auto pub_key_handle = std::shared_ptr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
if (!pub_key_handle)
{
return false;
}
RSA_up_ref(rsa);
EVP_PKEY_assign_RSA(pub_key_handle.get(), rsa);
std::string decoded_header(token, 0, token.find('.'));
std::string decoded_body;
decoded_body.append(token.begin()+ token.find('.')+1, token.begin() + token.rfind('.')-1);
std::string sig;
sig.append(token.begin() + token.rfind('.') + 1, token.end());
std::string sig_decoded;
base64_decode(sig.c_str(), sig.size(), sig_decoded);
EVP_MD_CTX* l_ctx = EVP_MD_CTX_create();
EVP_MD_CTX_init(l_ctx);
EVP_PKEY_CTX *pctx;
if (1 != EVP_DigestVerifyInit(l_ctx, /*&pctx*/nullptr,
EVP_sha256(), nullptr, pub_key_handle.get())) return false;
//pub_key_handle.reset();
if (1 != EVP_DigestVerifyUpdate(l_ctx, reinterpret_cast<const unsigned char*>(decoded_header.data()), decoded_header.length())) return false;
if (1 != EVP_DigestVerifyUpdate(l_ctx, ".", 1)) return false;
if (1 != EVP_DigestVerifyUpdate(l_ctx, reinterpret_cast<const unsigned char*>(decoded_body.data()), decoded_body.length())) return false;
if(1 == EVP_DigestVerifyFinal(l_ctx, reinterpret_cast<const unsigned char*>(sig_decoded.data()), sig_decoded.length())) return true;
// ERR_print_errors_fp(stdout);
ERR_load_crypto_strings();
char err[130];
while(auto e = ERR_get_error())
{
ERR_error_string(e, err);
fprintf(stderr, "Error verifying message: %s\n", err);
}
return false;
}
The error is:
header: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
body: eyJhdWQiOlsiYXBpLXJlc291cmNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNTYzNDUwODkzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMzVhYmVjZDEtNjcxMi00M2M3LWE4MDItZjg3MGYzMTY4MmI0IiwiY2xpZW50X2lkIjoidGVzdCJ
sig: ZCXtI2nN+d0Cn5dgb3K9JMI41nrEaK/AVSMRG9c5cyZqXpnMQETfGcDEs0jPzmRh+jDc+Kuq53naOtjkItMcR/vYPn72dKZ4Fpp8mvOAZXypkVCLzof3Lsxrtqq9G3V4LNTuOHiXW/q+9mEu51zWg1HDr1+rSt3YXkFFSWp5e4MWS2TNP1MB7lBbZC+kdMZ/GqZ9lrfNo2YqJR7tqcHOrfOmFTzqxVivEB8s+A0iEv/MwdlS6LpJBKU9+d94i1P9Lsqzlg7b/0ekRoYJEG4DXeNp2zxxBxZ1u3FBlIbyJoOGDmX+EU4A5eh2RlDdEvG1YF/zcMARpP1bFV86WTSOuQ==
Error verifying message: error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
error:04091068:rsa routines:INT_RSA_VERIFY:bad signature