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的根本原因。
代码的其他潜在问题及修正
缓冲区长度硬编码不合理
不要硬编码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 ""; }错误信息过于笼统
加入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
相关产品推荐
相关产品推荐

