如何使用OpenSSL EVP函数实现RSA公钥加密?
用OpenSSL EVP高级函数实现RSA公钥加密的正确姿势
你找不到对应RSA的EVP_CIPHER类型是完全正常的——因为RSA作为非对称加密算法,在OpenSSL的EVP框架里不属于对称加密的EVP_CIPHER体系,而是归属于非对称加密的EVP_PKEY体系。这是两类加密算法在设计逻辑和使用场景上的本质区别导致的:
- AES这类对称加密算法,核心是通过固定的加密模式(比如CBC、GCM)对数据流进行分段处理,所以用
EVP_CIPHER来描述算法和模式,配合EVP_EncryptInit_ex()/EVP_EncryptUpdate()/EVP_EncryptFinal_ex()来处理流式数据; - RSA这类非对称加密算法,通常用于加密小数据(比如对称密钥),它的加密逻辑是基于密钥对的数学运算,所以使用
EVP_PKEY(代表非对称密钥)和对应的上下文EVP_PKEY_CTX来操作,不需要EVP_CIPHER。
具体实现步骤&代码示例
下面是一个完整的用EVP高级函数实现RSA公钥加密的示例,包含公钥加载、加密操作和资源清理:
1. 核心逻辑说明
RSA公钥加密在EVP框架中主要使用EVP_PKEY_encrypt()函数(因为RSA加密通常不需要流式处理小数据),如果要处理大文件,正确的做法是混合加密:用AES加密文件,再用RSA加密AES密钥——这也是实际场景的标准用法。
2. 代码示例
#include <openssl/evp.h> #include <openssl/pem.h> #include <openssl/err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 错误处理辅助函数 void handle_errors(void) { ERR_print_errors_fp(stderr); abort(); } // RSA公钥加密函数 int rsa_public_encrypt(const unsigned char *plaintext, int plaintext_len, const char *pub_key_path, unsigned char *ciphertext) { EVP_PKEY *pub_key = NULL; EVP_PKEY_CTX *ctx = NULL; int ciphertext_len = 0; // 1. 从PEM文件加载RSA公钥 FILE *pub_key_file = fopen(pub_key_path, "r"); if (!pub_key_file) { perror("Failed to open public key file"); return -1; } pub_key = PEM_read_PUBKEY(pub_key_file, NULL, NULL, NULL); fclose(pub_key_file); if (!pub_key) { fprintf(stderr, "Failed to load public key\n"); handle_errors(); } // 2. 创建并初始化EVP_PKEY上下文 ctx = EVP_PKEY_CTX_new(pub_key, NULL); if (!ctx) { handle_errors(); } if (EVP_PKEY_encrypt_init(ctx) <= 0) { handle_errors(); } // 3. 设置加密参数(比如使用OAEP padding,比PKCS#1 v1.5更安全) if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) { handle_errors(); } // 4. 先获取加密后的数据长度 if (EVP_PKEY_encrypt(ctx, NULL, &ciphertext_len, plaintext, plaintext_len) <= 0) { handle_errors(); } // 5. 执行加密操作 if (EVP_PKEY_encrypt(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len) <= 0) { handle_errors(); } // 6. 清理资源 EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pub_key); return ciphertext_len; } int main(void) { // 初始化OpenSSL错误处理和算法库 OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); const unsigned char *plaintext = "Hello, RSA Encryption with EVP!"; int plaintext_len = strlen((char *)plaintext); unsigned char ciphertext[256]; // 2048位RSA加密后最多256字节 // 替换为你的公钥文件路径 int cipher_len = rsa_public_encrypt(plaintext, plaintext_len, "public_key.pem", ciphertext); if (cipher_len > 0) { printf("Encryption successful! Ciphertext length: %d bytes\n", cipher_len); } // 清理OpenSSL资源 EVP_cleanup(); ERR_free_strings(); return 0; }
关键注意事项
- 明文长度限制:RSA加密的明文长度不能超过
密钥长度/8 - padding长度,比如2048位RSA用OAEP padding时,最多能加密245字节左右。如果要加密大文件,一定要用混合加密方案; - Padding选择:优先使用
RSA_PKCS1_OAEP_PADDING,它比默认的RSA_PKCS1_PADDING更安全,能抵抗更多攻击; - 资源清理:所有OpenSSL创建的结构(
EVP_PKEY、EVP_PKEY_CTX等)都要记得调用对应的_free函数释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者void.pointer
相关产品推荐
相关产品推荐

