Если вы сделали некоторую базовую проверку ошибок, вы увидите, что ваша функция createPublicRSA возвращает nullptr.Это связано с тем, что PEM_read_bio_RSA_PUBKEY ожидает появления новых строк, а строка publicKey не имеет их.
Если вы измените ее на наличие новых строк, она должна быть в состоянии создать штраф RSA.
например
std::string publicKey = "-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\n"\
"vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\n"\
"aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\n"\
"tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\n"\
"e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\n"\
"V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\n"\
"MwIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
Также ваш код не будет работать, так как вам не нужно «кодировать» заголовок и основной текст, но вам нужно «base64url декодировать» подпись, так как она должна быть двоичным значениемчтобы проверка работала.
У меня работает следующий код:
bool RSAVerifySignature(RSA* rsa, std::string const& token)
{
auto const pub_key_handle = make_handle(EVP_PKEY_new(), EVP_PKEY_free);
if (!pub_key_handle)
{
RSA_free(rsa);
return false;
}
EVP_PKEY_assign_RSA(pub_key_handle.get(), rsa);
std::vector<std::string> token_parts;
split(token, token_parts, '.');
if (token_parts.size() != 3) return false;
auto& decoded_header = token_parts[0];
auto& decoded_body = token_parts[1];
auto sig_decoded = base64_url_decode(token_parts[2]);
auto const rsa_verify_ctx = make_handle(EVP_MD_CTX_new(), EVP_MD_CTX_free);
if (!rsa_verify_ctx) return false;
if (1 != EVP_DigestVerifyInit(rsa_verify_ctx.get(), nullptr, EVP_sha256(), nullptr, pub_key_handle.get())) return false;
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(decoded_header.data()), decoded_header.length())) return false;
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), ".", 1)) return false;
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(decoded_body.data()), decoded_body.length())) return false;
return 1 == EVP_DigestVerifyFinal(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(sig_decoded.data()), sig_decoded.length());
}
ОБНОВЛЕНИЕ:
Приведенный выше код предполагает, что вы используете указатель RSA (т. е. передаетеправо собственности на RSA, и он будет освобожден при выходе из функции).
В ответе Dmity он неправильно полагает, что он может передать указатель RSA на EVP_PKEY_assign_RSA несколько раз.Это делает его причиной того, что вы не можете освободить указатель EVP_PKEY, так как первое освобождение будет работать, а также уничтожит указатель RSA.Второе освобождение вызовет ошибку sib, о которой он говорит, так как указатель RSA уже освобожден.Чтобы изменить приведенный выше пример так, чтобы он не использовал указатель RSA, нам нужно увеличить внутреннюю ссылку RSA, чтобы свободный от указателя EVP_PKEY не освободил указатель RSA, а просто уменьшил его с помощью функции RSA_up_ref.
например
bool RSAVerifySignature(RSA* rsa, std::string const& token)
{
auto pub_key_handle = make_handle(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::vector<std::string> token_parts;
split(token, token_parts, '.');
if (token_parts.size() != 3) return false;
auto& decoded_header = token_parts[0];
auto& decoded_body = token_parts[1];
auto sig_decoded = base64_url_decode(token_parts[2]);
///auto const rsa_verify_ctx = make_handle(EVP_MD_CTX_new(), EVP_MD_CTX_free);
auto const rsa_verify_ctx = make_handle(EVP_MD_CTX_new(), EVP_MD_CTX_free);
if (!rsa_verify_ctx) return false;
EVP_PKEY_CTX *pctx;
if (1 != EVP_DigestVerifyInit(rsa_verify_ctx.get(), &pctx, EVP_sha256(), nullptr, pub_key_handle.get())) return false;
pub_key_handle.reset();
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(decoded_header.data()), decoded_header.length())) return false;
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), ".", 1)) return false;
if (1 != EVP_DigestVerifyUpdate(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(decoded_body.data()), decoded_body.length())) return false;
return 1 == EVP_DigestVerifyFinal(rsa_verify_ctx.get(), reinterpret_cast<unsigned char*>(sig_decoded.data()), sig_decoded.length());
}
И тестовый код вызова теперь может это делать и не вызывать ошибку sig:
auto public_rsa = make_handle(createPublicRSA(publicKey), RSA_free);
if(!public_rsa) return false;
for(auto i = 0; i < 5; ++i)
{
if(!RSAVerifySignature(public_rsa.get(), token)) return false;
}
public_rsa.reset();