OpenSSL 3.0 C++中使用RSA公钥加密的现代方法(替代弃用的RSA_public_encrypt)
OpenSSL 3.0+ 替代 RSA_public_encrypt 的现代 RSA 公钥加密方式
OpenSSL 3.0 弃用 RSA_public_encrypt 这类低阶RSA API,核心原因是这类接口要求开发者手动处理填充、密钥管理等细节,极易引入安全漏洞。现代推荐使用EVP高层加密API——它封装了密码学操作的通用逻辑,支持更安全的填充方式(如RSA-OAEP),且兼容性、可维护性更强。
核心实现步骤(C语言示例)
以下是使用EVP API实现RSA公钥加密的完整流程,以PEM格式公钥为例:
加载RSA公钥
从PEM文件或内存中读取公钥,解析为EVP_PKEY对象(OpenSSL 3.0中统一用该结构管理各类密钥)。初始化加密上下文
创建EVP_PKEY_CTX上下文,绑定公钥并指定加密算法(优先选RSA-OAEP,比传统PKCS#1 v1.5更安全)。执行加密操作
调用EVP_PKEY_encrypt系列函数完成加密,自动处理填充逻辑。清理资源
释放所有OpenSSL对象,避免内存泄漏。
完整代码示例
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/evp.h> #include <openssl/pem.h> #include <openssl/err.h> // 错误处理辅助函数 static void handle_errors(void) { ERR_print_errors_fp(stderr); abort(); } // RSA公钥加密函数(使用RSA-OAEP填充) int rsa_public_encrypt(const unsigned char *plaintext, size_t plaintext_len, const char *pub_key_path, unsigned char **ciphertext) { EVP_PKEY *pub_key = NULL; EVP_PKEY_CTX *ctx = NULL; size_t ciphertext_len = 0; // 1. 加载PEM格式公钥 FILE *pub_file = fopen(pub_key_path, "r"); if (!pub_file) { perror("Failed to open public key file"); return -1; } pub_key = PEM_read_PUBKEY(pub_file, NULL, NULL, NULL); fclose(pub_file); if (!pub_key) { fprintf(stderr, "Failed to read public key\n"); handle_errors(); } // 2. 创建并初始化加密上下文 ctx = EVP_PKEY_CTX_new(pub_key, NULL); if (!ctx) handle_errors(); if (EVP_PKEY_encrypt_init(ctx) <= 0) handle_errors(); // 指定使用RSA-OAEP填充(默认SHA-256,可手动指定哈希算法) if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) handle_errors(); // 可选:指定OAEP使用的哈希算法,比如SHA-512 // if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha512()) <= 0) handle_errors(); // 3. 获取加密后数据长度 if (EVP_PKEY_encrypt(ctx, NULL, &ciphertext_len, plaintext, plaintext_len) <= 0) handle_errors(); // 分配内存存储密文 *ciphertext = malloc(ciphertext_len); if (!*ciphertext) { fprintf(stderr, "Memory allocation failed\n"); goto cleanup; } // 执行加密 if (EVP_PKEY_encrypt(ctx, *ciphertext, &ciphertext_len, plaintext, plaintext_len) <= 0) handle_errors(); cleanup: // 清理资源 EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pub_key); return ciphertext_len; } // 测试用例 int main(void) { const unsigned char *plaintext = "Hello, RSA-OAEP Encryption!"; const char *pub_key_path = "public_key.pem"; unsigned char *ciphertext = NULL; int ciphertext_len; // 初始化OpenSSL错误处理 OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); ciphertext_len = rsa_public_encrypt(plaintext, strlen((char*)plaintext), pub_key_path, &ciphertext); if (ciphertext_len <= 0) { fprintf(stderr, "Encryption failed\n"); return 1; } printf("Encrypted data length: %d bytes\n", ciphertext_len); // 可将密文写入文件或传输,此处省略 free(ciphertext); ERR_free_strings(); return 0; }
关键注意事项
- 填充方式选择:优先使用
RSA_PKCS1_OAEP_PADDING(RSA-OAEP),它比旧的RSA_PKCS1_PADDING(PKCS#1 v1.5)更安全,能抵御选择密文攻击(CCA)。 - 数据长度限制:RSA加密仅适合小数据(比如对称密钥),2048位RSA密钥使用OAEP填充时,最大可加密
2048/8 - 2*哈希长度 - 2字节(SHA-256对应245字节)。如果要加密大文件,正确流程是:- 生成随机对称密钥(比如AES-256)
- 用AES加密大文件
- 用RSA公钥加密AES密钥
- 错误处理:必须检查每个OpenSSL函数的返回值,避免忽略潜在的安全问题。
- OpenSSL 3.0兼容性:如果使用OpenSSL 3.0的provider机制,可通过
EVP_PKEY_CTX_set_provider指定加密提供者(默认是default provider,已包含RSA算法)。
内容的提问来源于stack exchange,提问作者Mikołaj Gogola
相关产品推荐
相关产品推荐

