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

基于OpenSSL与Curl的C++客户端-服务器端到端加密解密问题排查

RSA OAEP解密错误排查与修复

基于OpenSSL实现C++客户端与PHP服务器的端到端加密流程如下:

  1. PHP脚本生成RSA公私钥对
  2. C++客户端保存自身密钥并获取服务器公钥
  3. 客户端用服务器公钥加密字符串无异常,但服务器用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:45:37