基于OpenSSL与Curl的C++客户端-服务器端到端加密解密问题排查
RSA OAEP解密错误排查与修复
基于OpenSSL实现C++客户端与PHP服务器的端到端加密流程如下:
- PHP脚本生成RSA公私钥对
- C++客户端保存自身密钥并获取服务器公钥
- 客户端用服务器公钥加密字符串无异常,但服务器用PHP脚本解密时抛出错误:
OpenSSL Error: error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
已确认客户端与服务器间传输的数据无差异,请求解决该解密故障。
相关代码
C++客户端主函数
int main() { // 创建GUI menu::create_h_window("test window"); menu::create_device(); menu::create_imgui(); // 密钥交换 key_exchange exchanger; exchanger.exchange_keys(); std::string test_message = "This is a test message"; std::string encrypted_message = exchanger.encrypt_message(test_message, exchanger.server_public_key); curl_request request; CURL* curl = curl_easy_init(); char* encoded_message = curl_easy_escape(curl, exchanger.base64_encode(encrypted_message).c_str(), 0); std::string post_fields = "message=" + std::string(encoded_message); curl_free(encoded_message); std::string server_response = request.post_request("https://example.org/test_encryption.php", post_fields); while (menu::b_is_running) { menu::begin_render(); menu::render(); menu::end_render(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); } // 销毁GUI menu::destroy_imgui(); menu::destroy_device(); menu::destroy_h_window(); return 0; }
PHP服务器解密脚本
<?php error_reporting(E_ALL); ini_set('display_errors', 1); if (!isset($_POST['message'])) { echo "Error: Message not received"; exit; } if (!file_exists('server_private_key.pem')) { echo "Error: Private key file not found"; exit; } $private_key = file_get_contents('server_private_key.pem'); if (!$private_key) { echo "Error: Invalid private key"; exit; } $encrypted_message = base64_decode($_POST['message']); if (!openssl_private_decrypt($encrypted_message, $decrypted_message, $private_key, OPENSSL_PKCS1_OAEP_PADDING)) { while ($msg = openssl_error_string()) { echo "OpenSSL Error: " . $msg . "\n"; } exit; } echo $decrypted_message; ?>
C++客户端加密函数
std::string encrypt_message(const std::string& message, const std::string& other_public_key) { BIO* bio = BIO_new_mem_buf(other_public_key.data(), other_public_key.size()); EVP_PKEY* evp_pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (evp_pkey == nullptr) { unsigned long err = ERR_get_error(); char* err_str = ERR_error_string(err, NULL); std::cout << "Error: " << err_str << std::endl; } RSA* other_rsa = EVP_PKEY_get1_RSA(evp_pkey); BIO_free(bio); int rsa_size = RSA_size(other_rsa); unsigned char* encrypted = new unsigned char[rsa_size]; int encrypted_len = RSA_public_encrypt(message.size(), reinterpret_cast<const unsigned char*>(message.data()), encrypted, other_rsa, RSA_PKCS1_OAEP_PADDING); std::string encrypted_str(reinterpret_cast<char*>(encrypted), encrypted_len); delete[] encrypted; RSA_free(other_rsa); EVP_PKEY_free(evp_pkey); return encrypted_str; }
故障排查与修复方案
OAEP解码错误通常由哈希算法不匹配、密钥不对应、数据编码/传输损坏三类原因导致,按以下步骤逐一排查:
1. 强制哈希算法一致性
OpenSSL的RSA_PKCS1_OAEP_PADDING默认使用SHA-1,但新版本OpenSSL/PHP可能默认使用SHA-256,两端必须统一:
- C++端:改用EVP接口显式指定SHA-256哈希(替代原
RSA_public_encrypt):std::string encrypt_message(const std::string& message, const std::string& other_public_key) { BIO* bio = BIO_new_mem_buf(other_public_key.data(), other_public_key.size()); EVP_PKEY* evp_pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (!evp_pkey) { // 错误处理逻辑 } EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(evp_pkey, NULL); EVP_PKEY_encrypt_init(ctx); // 显式配置OAEP参数 EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()); EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()); size_t out_len; EVP_PKEY_encrypt(ctx, NULL, &out_len, (const unsigned char*)message.data(), message.size()); unsigned char* encrypted = new unsigned char[out_len]; EVP_PKEY_encrypt(ctx, encrypted, &out_len, (const unsigned char*)message.data(), message.size()); std::string encrypted_str((char*)encrypted, out_len); delete[] encrypted; EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(evp_pkey); BIO_free(bio); return encrypted_str; } - PHP端:解密时同步指定SHA-256哈希(PHP 7.1+支持):
// 替换原openssl_private_decrypt调用 $oaep_options = array( 'digest_alg' => 'sha256', 'mgf1_digest_alg' => 'sha256' ); if (!openssl_private_decrypt($encrypted_message, $decrypted_message, $private_key, OPENSSL_PKCS1_OAEP_PADDING, $oaep_options)) { // 错误处理逻辑 }
2. 验证密钥对匹配性
确认客户端使用的服务器公钥与服务器解密用私钥属于同一密钥对:
执行以下命令对比模数:
# 查看公钥模数 openssl rsa -pubin -in server_public_key.pem -modulus -noout # 查看私钥模数 openssl rsa -in server_private_key.pem -modulus -noout
若模数不同,说明密钥不匹配,重新生成或交换正确密钥。
3. 检查数据编码完整性
- 替换客户端自定义
base64_encode为OpenSSL标准实现(EVP_EncodeInit/EVP_EncodeUpdate/EVP_EncodeFinal),避免自定义编码逻辑出错。 - 确认
curl_easy_escape转义后的字符,PHP的$_POST能完全解码,若手动处理需确保无字符丢失。
4. 控制明文长度
RSA OAEP加密的明文长度上限为:密钥长度/8 - 2*哈希长度 - 2。例如2048位密钥配合SHA-256时,最大明文长度为190字节。若明文过长,需改用混合加密方案:用RSA加密对称密钥,再用对称密钥加密明文。
内容的提问来源于stack exchange,提问作者Authority
相关产品推荐
相关产品推荐

