C++中如何从私钥获取EVP_PKEY?RSA加密代码求助
问题解决:私钥生成EVP_PKEY对象 + RSA加密代码段错误修复
一、段错误核心原因
你遇到的段错误大概率是两个问题导致:
- 传入的
pkey无效:要么是NULL,要么是未正确初始化的EVP_PKEY对象,导致EVP_PKEY_CTX_new创建的上下文内部状态异常。 - 操作逻辑错误:
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; }
四、额外排查建议
- 调用加密函数前,务必检查传入的
pkey是否为非空且有效(可打印pkey地址确认)。 - 启用OpenSSL错误打印(
ERR_print_errors_fp(stderr)),能获取更精准的错误定位信息。 - 如果实际需求是签名而非加密,需替换为
EVP_PKEY_sign_init、EVP_PKEY_sign等签名接口,用私钥完成操作。
内容的提问来源于stack exchange,提问作者topaz23
相关产品推荐
相关产品推荐

