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

C++中如何从私钥获取EVP_PKEY?RSA加密代码求助

问题解决:私钥生成EVP_PKEY对象 + RSA加密代码段错误修复

一、段错误核心原因

你遇到的段错误大概率是两个问题导致:

  1. 传入的pkey无效:要么是NULL,要么是未正确初始化的EVP_PKEY对象,导致EVP_PKEY_CTX_new创建的上下文内部状态异常。
  2. 操作逻辑错误:EVP_PKEY_encrypt_init是用于公钥加密的接口,私钥不能用于这个操作——RSA标准流程是公钥加密、私钥解密;私钥的典型用法是签名(对应EVP_PKEY_sign_init),强行用私钥调用加密初始化会触发内部错误甚至崩溃。

二、如何从私钥获取EVP_PKEY对象

以下两种常见场景的实现:

1. 从PEM格式文件加载私钥

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <openssl/err.h>

EVP_PKEY* load_rsa_private_key(const char* pem_file_path) {
    FILE* fp = fopen(pem_file_path, "rb");
    if (!fp) {
        perror("打开私钥文件失败");
        return NULL;
    }

    // 如果私钥有密码,需替换最后两个NULL为密码回调函数和参数
    EVP_PKEY* pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
    fclose(fp);

    if (!pkey) {
        fprintf(stderr, "加载私钥失败\n");
        ERR_print_errors_fp(stderr); // 打印OpenSSL详细错误信息
    }
    return pkey;
}

2. 从内存中的PEM数据加载私钥

EVP_PKEY* load_rsa_private_key_from_mem(const unsigned char* pem_data, size_t pem_len) {
    BIO* bio = BIO_new_mem_buf(pem_data, pem_len);
    if (!bio) {
        fprintf(stderr, "创建内存BIO失败\n");
        return NULL;
    }

    EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
    BIO_free(bio);

    if (!pkey) {
        fprintf(stderr, "加载内存私钥失败\n");
        ERR_print_errors_fp(stderr);
    }
    return pkey;
}

三、修正后的RSA加密代码(公钥加密)

如果你的需求是加密,必须使用公钥;如果是签名,请更换为EVP_PKEY_sign相关接口。以下是修正后的加密代码,修复了内存泄漏、参数安全、返回值缺失等问题:

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 加载公钥(加密必须用公钥)
EVP_PKEY* load_rsa_public_key(const char* pem_file_path) {
    FILE* fp = fopen(pem_file_path, "rb");
    if (!fp) {
        perror("打开公钥文件失败");
        return NULL;
    }

    EVP_PKEY* pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
    fclose(fp);

    if (!pkey) {
        fprintf(stderr, "加载公钥失败\n");
        ERR_print_errors_fp(stderr);
    }
    return pkey;
}

// RSA加密函数(公钥加密)
int rsaEvpEncrypt(EVP_PKEY* pkey, const unsigned char* data, size_t inlen, 
                  unsigned char** out_data, size_t* outlen) {
    // 参数合法性检查
    if (!pkey || !data || !out_data || !outlen) {
        return -1;
    }

    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (!ctx) {
        fprintf(stderr, "创建加密上下文失败\n");
        ERR_print_errors_fp(stderr);
        return 1000;
    }

    // 初始化加密上下文(公钥加密)
    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        fprintf(stderr, "初始化加密上下文失败\n");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return 5;
    }

    // 设置OAEP填充(更安全的填充方式)
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
        fprintf(stderr, "设置RSA填充失败\n");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return 6;
    }

    // 获取加密后的数据长度
    if (EVP_PKEY_encrypt(ctx, NULL, outlen, data, inlen) <= 0) {
        fprintf(stderr, "计算加密数据长度失败\n");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return 7;
    }

    // 分配加密内存
    *out_data = (unsigned char*)OPENSSL_malloc(*outlen);
    if (!*out_data) {
        fprintf(stderr, "分配加密内存失败\n");
        EVP_PKEY_CTX_free(ctx);
        return 8;
    }

    // 执行加密
    if (EVP_PKEY_encrypt(ctx, *out_data, outlen, data, inlen) <= 0) {
        fprintf(stderr, "加密操作失败\n");
        ERR_print_errors_fp(stderr);
        OPENSSL_free(*out_data);
        *out_data = NULL;
        EVP_PKEY_CTX_free(ctx);
        return 9;
    }

    // 释放上下文资源
    EVP_PKEY_CTX_free(ctx);
    return 0;
}

// 使用示例
int main() {
    EVP_PKEY* pub_key = load_rsa_public_key("public_key.pem");
    if (!pub_key) {
        return 1;
    }

    const char* plaintext = "Hello, RSA Encryption!";
    unsigned char* ciphertext = NULL;
    size_t cipher_len = 0;

    int ret = rsaEvpEncrypt(pub_key, (const unsigned char*)plaintext, 
                            strlen(plaintext), &ciphertext, &cipher_len);
    if (ret != 0) {
        fprintf(stderr, "加密失败,错误码:%d\n", ret);
        EVP_PKEY_free(pub_key);
        return ret;
    }

    printf("加密成功,密文长度:%zu\n", cipher_len);

    // 释放资源
    OPENSSL_free(ciphertext);
    EVP_PKEY_free(pub_key);
    return 0;
}

四、额外排查建议

  1. 调用加密函数前,务必检查传入的pkey是否为非空且有效(可打印pkey地址确认)。
  2. 启用OpenSSL错误打印(ERR_print_errors_fp(stderr)),能获取更精准的错误定位信息。
  3. 如果实际需求是签名而非加密,需替换为EVP_PKEY_sign_init、EVP_PKEY_sign等签名接口,用私钥完成操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:05:07