You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenSSL中C++调用EVP_PKEY_decrypt解密失败问题排查

问题排查:OpenSSL公钥解密失败(EVP_PKEY_decrypt返回0)

问题场景

生成RSA密钥对后,用私钥加密一段字符串,尝试用公钥解密时,EVP_PKEY_decrypt函数返回0,无法完成解密,即使密钥对正确也无法解决。提供的解密函数代码如下:

std::string decryptWithPublicKey(const std::string& publicKey, const std::string& encryptedText) {
    BIO* publicKeyBio = BIO_new_mem_buf((void*)publicKey.c_str(), -1);
    EVP_PKEY* pkey = PEM_read_bio_PUBKEY(publicKeyBio, NULL, NULL, NULL);
    BIO_free_all(publicKeyBio);

    if (pkey == NULL) {
        std::cerr << "Error reading public key" << std::endl;
        return "";
    }

    //size_t decryptedTextLen = EVP_PKEY_size(pkey);
    //size_t decryptedTextLen = encryptedText.size();
    size_t decryptedTextLen = 2048;
    //std::cout<<"decryptedTextLen:"<<decryptedTextLen<<"\n";
    std::string decryptedText(decryptedTextLen, '\0');

    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
        std::cerr << "Error initializing decryption context" << std::endl;
        EVP_PKEY_free(pkey);
        EVP_PKEY_CTX_free(ctx);
        return "";
    }
    
    
    //std::cout<<"decryptedTextLen:"<<decryptedTextLen<<"\n";
    if (EVP_PKEY_decrypt(ctx, (unsigned char*)decryptedText.data(), &decryptedTextLen, (const unsigned char*)encryptedText.c_str(), encryptedText.size()) <= 0) {
        std::cerr << "Error decrypting data2" << std::endl;
        EVP_PKEY_free(pkey);
        EVP_PKEY_CTX_free(ctx);
        return "";
    }

    EVP_PKEY_free(pkey);
    EVP_PKEY_CTX_free(ctx);

    decryptedText.resize(decryptedTextLen);
    return decryptedText;
}

核心问题分析

  • RSA密钥使用逻辑完全错误:
    RSA的核心规则是:公钥加密,私钥解密;私钥签名,公钥验签。你用私钥加密的内容,本质属于签名操作,需要用公钥通过验签接口(EVP_PKEY_verify)验证,而不是用解密接口EVP_PKEY_decrypt。EVP_PKEY_decrypt仅用于解密由公钥加密的数据,用公钥解私钥加密的内容,逻辑上不成立,这是导致函数返回0的根本原因。

代码的其他潜在问题及修正

  1. 缓冲区长度硬编码不合理
    不要硬编码2048作为缓冲区长度,正确做法是先调用EVP_PKEY_decrypt传入NULL获取所需长度,再分配内存:

    size_t decryptedLen = 0;
    // 先获取解密所需的长度
    if (EVP_PKEY_decrypt(ctx, NULL, &decryptedLen, (const unsigned char*)encryptedText.c_str(), encryptedText.size()) <= 0) {
        std::cerr << "Failed to get decrypted length" << std::endl;
        printOpenSSLError();
        EVP_PKEY_free(pkey);
        EVP_PKEY_CTX_free(ctx);
        return "";
    }
    std::string decryptedText(decryptedLen, '\0');
    // 再执行解密
    if (EVP_PKEY_decrypt(ctx, (unsigned char*)decryptedText.data(), &decryptedLen, (const unsigned char*)encryptedText.c_str(), encryptedText.size()) <= 0) {
        std::cerr << "Error decrypting data" << std::endl;
        printOpenSSLError();
        EVP_PKEY_free(pkey);
        EVP_PKEY_CTX_free(ctx);
        return "";
    }
    
  2. 错误信息过于笼统
    加入OpenSSL错误栈打印,能获取具体错误原因,方便排查:

    void printOpenSSLError() {
        unsigned long errCode;
        while ((errCode = ERR_get_error()) != 0) {
            char errMsg[1024];
            ERR_error_string(errCode, errMsg);
            std::cerr << "OpenSSL Error: " << errMsg << std::endl;
        }
    }
    

    在每次错误输出后调用该函数,就能看到类似"invalid padding"或"wrong key type"这类具体错误。

修正方向

  • 如果需求是加密后仅允许私钥持有者解密:调整流程为公钥加密,私钥解密;
  • 如果需求是验证内容由私钥持有者发出:替换为签名验签流程,用EVP_PKEY_sign生成签名,EVP_PKEY_verify验证签名。

内容的提问来源于stack exchange,提问作者Higher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:34:59