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

OpenSSL 3中RSA的EVP_PKEY_encrypt_init调用触发段错误

OpenSSL 3 RSA加密解密程序问题排查

问题概述

我在C++环境下使用OpenSSL 3开发一个简单程序,功能包括读取字节数据、动态生成RSA密钥对、公钥加密及解密。但调用EVP_PKEY_encrypt_init时触发段错误,通过gdb调试发现main函数返回5,始终无法解决问题。我主要参考OpenSSL Man3文档编写代码,但对整体实现逻辑仍有困惑,确定代码存在错误但无法定位,希望得到帮助。

最小可复现代码(MVCE)

#define OPENSSL_NO_DEPRECATED
#include <iostream>
#include <cstring>
#include <cstdio>

#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>

constexpr int KEY_BITS = 4096;

void cleanup(EVP_PKEY_CTX * ctx, EVP_PKEY * pkey, unsigned char * encrypted, unsigned char * decrypted) {
    ERR_print_errors_fp(stderr);
    if (ctx) { EVP_PKEY_CTX_free(ctx); }
    if (pkey) { EVP_PKEY_free(pkey); }
    if (encrypted) { OPENSSL_free(encrypted); }
    if (decrypted) { OPENSSL_free(decrypted); }
}


int main(int argc, const char ** argv) {
    EVP_PKEY_CTX * ctx = NULL;
    EVP_PKEY * pkey = NULL;

    if (argc != 2) {
        return 101;
    }

    std::cout << "MESSAGE: " << argv[1]  << std::endl;

    const size_t inlen = strlen(argv[1]);
    size_t outlen = 0;
    size_t doutlen = 0;

    unsigned char * encrypted = NULL;
    unsigned char * decrypted = NULL;

    // 创建上下文
    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx) { 
        cleanup(ctx, pkey, encrypted, decrypted);
        return 1;
    }
    // 初始化密钥生成器
    if (EVP_PKEY_keygen_init(ctx) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 2;
    }

    // 配置密钥生成参数
    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KEY_BITS) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 3;
    }

    // 生成密钥对
    if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 4;
    }
    // std::cout << "PRIVATE KEY:" << std::endl;
    // EVP_PKEY_print_private_fp(stdout, pkey, 0, NULL);
    // std::cout << "PUBLIC KEY:" << std::endl;
    // EVP_PKEY_print_public_fp(stdout, pkey, 0, NULL);

    // 初始化加密上下文
    if (EVP_PKEY_encrypt_init(ctx) <= 0) { // <- 触发段错误的行
       cleanup(ctx, pkey, encrypted, decrypted);
       return 5;
    }

    // 设置填充模式
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 6;
    }

    // 计算加密缓冲区大小并执行加密
    if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char *)argv[1], inlen) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 7;
    }

    encrypted = (unsigned char *)OPENSSL_malloc(outlen);
    if (!encrypted) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 8;
    }

    if (EVP_PKEY_encrypt(ctx, encrypted, &outlen, (const unsigned char *)argv[1], inlen) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 9;
    }

    std::cout << "ENCRYPTED: " << (char *)encrypted << std::endl;

    // 计算解密缓冲区大小、分配内存并执行解密
    if (EVP_PKEY_decrypt(ctx, NULL, &doutlen, encrypted, outlen) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 17;
    }

    decrypted = (unsigned char *)OPENSSL_malloc(doutlen);
    if (!decrypted) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 18;
    }

    if (EVP_PKEY_decrypt(ctx, decrypted, &doutlen, encrypted, outlen) <= 0) {
        cleanup(ctx, pkey, encrypted, decrypted);
        return 19;
    }

    std::cout << "DECRYPTED: " << (char *)decrypted << std::endl;
    
    cleanup(ctx, pkey, encrypted, decrypted);
    return 0;
}

编译命令

g++ -g -Wextra -Werror test.cc $(pkg-config --cflags openssl) $(pkg-config --libs openssl) -o test; ./test sndlkwndl; echo $?

OpenSSL版本信息

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
built on: Wed Jan 31 18:43:23 2024 UTC
platform: debian-amd64
options:  bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -ffile-prefix-map=/build/openssl-l59e1T/openssl-3.0.2=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-3"
MODULESDIR: "/usr/lib/x86_64-linux-gnu/ossl-modules"
Seeding source: os-specific
CPUINFO: OPENSSL_ia32cap=0x7ffaf3bfffebffff:0x40000000029c6fbf

(通过Ubuntu 22.04LTS仓库的libssl-dev安装)

参考文档

  • OpenSSL 3.0官方EVP_PKEY_CTX_new文档
  • OpenSSL 1.1.1官方EVP_PKEY_decrypt文档
  • OpenSSL 1.1.1官方EVP_PKEY_encrypt文档

补充说明

编写问题时发现代码缺少EVP_PKEY_decrypt_init调用,但当前先专注解决加密阶段的段错误问题。


更新:段错误修复与新问题

根据建议,了解到OpenSSL上下文设计为轻量且生命周期短,不能复用同一上下文完成密钥生成、加密和解密操作,需为每个操作创建新上下文。按照以下代码修改后,段错误问题已解决:

cleanup(ctx, nullptr, nullptr, nullptr);

ctx = nullptr;
ctx = EVP_PKEY_CTX_new(pkey, nullptr);
if (!ctx) {
  cleanup(ctx, nullptr, nullptr, nullptr);
  return 1000;
} 

此外,补充了EVP_PKEY_decrypt_init的调用:

if (EVP_PKEY_decrypt_init(ctx) <= 0) {
   cleanup(ctx, pkey, encrypted, decrypted);
   return 5;
}

目前代码可正常编译,但解密结果与原消息不符,仍存在问题。另外,尝试复用加密上下文进行解密的方案也可运行,但解密结果仍不正确。

重要提示:推荐学习OpenSSL官方示例代码仓库的内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:06:02