分割故障在使用C 中的openssl使用公共密钥验证JWT令牌时



我是C 和OpenSSL的新手。我必须使用C 中的OpenSSL验证给定的JWT令牌(算法RS256(。我正在使用以下算法来验证JWT令牌。

// signature algorithm data = base64urlEncode( header ) + “.” + base64urlEncode( payload ) hashedData = hash( data, secret ) signature = base64urlEncode( hashedData )

我在Mac系统上,并使用G 编译我的代码。终端上的openssl version显示LibreSSL 2.6.5

// Assume that base64 encode and decode functions are available
bool RSAVerifySignature( RSA* rsa, std::string token, std::string pub_key) {
  std::vector<std::string> tokenParts;
  split(token, tokenParts, '.');
  std::string decoded_header = tokenParts[0];
  std::string header = base64_encode(reinterpret_cast<const unsigned char*>(decoded_header.c_str()),
    decoded_header.length());
  std::string decoded_body = tokenParts[1];
  std::string body = base64_encode(reinterpret_cast<const unsigned char*>(decoded_body.c_str()),
    decoded_body.length());

  std::string sig = tokenParts[2];
  EVP_PKEY* pubKey  = EVP_PKEY_new();
  EVP_PKEY_assign_RSA(pubKey, rsa);
  EVP_MD_CTX* m_RSAVerifyCtx = EVP_MD_CTX_create();
  if (1 != EVP_DigestVerifyInit(m_RSAVerifyCtx, NULL, EVP_sha256(), NULL, pubKey)) {
      printf("verify init failed....n");
  } else {
      printf("verify init passed....n");
  }
  if (1 != EVP_DigestVerifyUpdate(m_RSAVerifyCtx, (unsigned char *)header.data(), header.length())) {
      printf("DigestVerifyUpdate for header failed....n");
  } else {
          printf("DigestVerifyUpdate for header passed....n");
  }
  if (1 != EVP_DigestVerifyUpdate(m_RSAVerifyCtx, ".", 1)) {
      printf("DigestVerifyUpdate for dot failedn");
  } else {
      printf("DigestVerifyUpdate for dot passedn");
  }
  if (1 != EVP_DigestVerifyUpdate(m_RSAVerifyCtx, (unsigned char *)body.data(), body.length())) {
      printf("DigestVerifyUpdate for body failedn");
  } else {
      printf("DigestVerifyUpdate for body passedn");
  }
  int result = EVP_DigestVerifyFinal(m_RSAVerifyCtx, (unsigned char *)sig.data(), sig.length());
  return result;
}
RSA* createPublicRSA(std::string key) {
  RSA *rsa = NULL;
  BIO *keybio;
  const char* c_string = key.c_str();
  keybio = BIO_new_mem_buf((void*)c_string, -1);
  if (keybio==NULL) {
      return 0;
  }
  rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);
  return rsa;
}
int main()
{
    std::string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";
    std::string publicKey = "-----BEGIN PUBLIC KEY-----"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv"
"vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc"
"aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy"
"tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0"
"e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb"
"V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9"
"MwIDAQAB"
"-----END PUBLIC KEY-----";
    RSA* publicRSA = createPublicRSA(publicKey);
    bool result = RSAVerifySignature(publicRSA, token, publicKey);
    return 0;
}

我在EVP_DigestVerifyFinal呼叫上获得Segmentation fault: 11。我不知道我在哪里错了。请帮助。

如果您完成了一些基本错误检查,则会看到您的CreatePublicrsa函数返回NULLPTR。这是因为PEM_READ_BIO_RSA_PUBKEY期望看到新线,而您的publicKey字符串没有任何。

如果将其更改为具有新线,则应该能够创建RSA键。

,例如

    std::string publicKey = "-----BEGIN PUBLIC KEY-----n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvn"
"vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcn"
"aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIyn"
"tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0n"
"e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbn"
"V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9n"
"MwIDAQABn"
"-----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指针,而是使用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();

遵循CTX清理的代码和平

bool sha_validate( const EVP_MD* type, const std::string& input, const std::vector<unsigned char>& digest )
{
    if( !rsa )
        return false;
    EVP_PKEY* pub_key  = EVP_PKEY_new(); 
    EVP_PKEY_assign_RSA(pub_key, rsa);
    EVP_MD_CTX* rsa_verify_ctx = EVP_MD_CTX_create();
    auto ctx_free = scope_remove( [rsa_verify_ctx]() {
        EVP_MD_CTX_cleanup( rsa_verify_ctx );
        EVP_MD_CTX_destroy( rsa_verify_ctx );
    });
    if (EVP_DigestVerifyInit( rsa_verify_ctx,NULL, type, NULL, pub_key  ) <=0 )
        return false;
    if (EVP_DigestVerifyUpdate( rsa_verify_ctx, input.c_str(), input.size() ) <= 0)
        return false;
    return EVP_DigestVerifyFinal( rsa_verify_ctx, &digest[0], digest.size() ) == 1;
}
bool sha_validate( int type, const std::string& input, const std::vector<unsigned char>& digest )
{
     bool result = false;
     if( type & RsaOaep::SHA1 )
         result = sha_validate( EVP_sha1(), input, digest );
     if( !result && ( type & RsaOaep::SHA256 ) == static_cast<int>(RsaOaep::SHA256) )
         result = sha_validate( EVP_sha256(), input, digest );
     return result;
}
RSA* rsa = nullptr;

最新更新