从原理到代码:手把手教你用C语言和OpenSSL实现RSA分段加密与验签(附完整项目)
RSA分段加密与数字签名实战:OpenSSL C语言实现深度解析
在信息安全领域,RSA算法作为非对称加密的基石,其重要性不言而喻。但真正在C/C++项目中集成RSA功能时,开发者往往会遇到各种"魔鬼细节"——从内存管理到分段处理,从密钥加载到签名验证,每个环节都可能成为项目落地的绊脚石。本文将带您深入OpenSSL的RSA实现,用工业级代码解决实际问题。
1. RSA工程化实现的核心挑战
当我们需要处理超过117字节的数据时(对于1024位密钥),简单的单次加密调用就变得力不从心。这时,分段加密成为必选项,但随之而来的是一系列工程问题:
- 缓冲区管理:如何设计高效的输入/输出缓冲区
- 内存安全:避免加密过程中的内存泄漏
- 数据拼接:确保分段加密后的数据能正确重组
- 性能考量:大数据量下的加密效率优化
让我们先看一个典型的分段加密函数框架:
int rsa_encrypt(RSA* rsa, const unsigned char* in, int in_len, unsigned char** out, int* out_len) { int key_size = RSA_size(rsa); int block_size = key_size - 11; // PKCS#1 v1.5 padding int blocks = (in_len + block_size - 1) / block_size; *out = malloc(key_size * blocks); if (!*out) return 0; for (int i = 0; i < blocks; i++) { int curr_size = (i == blocks-1) ? in_len - i*block_size : block_size; int ret = RSA_public_encrypt(curr_size, in + i*block_size, *out + i*key_size, rsa, RSA_PKCS1_PADDING); if (ret != key_size) { free(*out); return 0; } } *out_len = key_size * blocks; return 1; }这个基础框架已经解决了几个关键问题:
- 自动计算需要的分段数量
- 合理分配输出缓冲区
- 处理最后一块可能不足的情况
- 错误处理和内存释放
2. OpenSSL RSA函数深度解析
2.1 核心API的工程考量
OpenSSL的RSA API看似简单,但实际使用中有许多细节需要注意:
| 函数 | 关键参数 | 常见陷阱 | 最佳实践 |
|---|---|---|---|
RSA_public_encrypt | flen, padding | 输入长度限制 | 检查返回值是否等于密钥长度 |
RSA_private_decrypt | flen, padding | 内存对齐问题 | 输出缓冲区预留RSA_size(rsa)空间 |
RSA_sign | type, m_len | 哈希算法匹配 | 确保使用相同的哈希算法验签 |
RSA_verify | type, siglen | 返回值检查 | 验证返回值等于1而非非零 |
特别需要注意的是填充方式的选择。RSA_PKCS1_PADDING是最常用的填充方案,但它会占用11字节的空间,这也是1024位密钥只能加密117字节的原因。
2.2 密钥加载的两种方式
OpenSSL支持从文件和内存加载密钥,两种方式各有适用场景:
文件加载方式:
RSA* load_rsa_private_key(const char* filename) { FILE* fp = fopen(filename, "rb"); if (!fp) return NULL; RSA* rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); fclose(fp); return rsa; }内存加载方式:
RSA* load_rsa_private_key_from_mem(const char* data, size_t len) { BIO* bio = BIO_new_mem_buf(data, len); if (!bio) return NULL; RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); BIO_free(bio); return rsa; }内存加载方式特别适合:
- 嵌入式系统等文件访问受限的环境
- 需要保护密钥不被写入磁盘的安全场景
- 动态生成的密钥数据
3. 分段加密的进阶实现
3.1 处理非对齐数据
实际项目中,我们常遇到数据长度不是块大小整数倍的情况。下面是一个健壮的分段处理实现:
int rsa_encrypt_ex(RSA* rsa, const unsigned char* in, int in_len, unsigned char** out, int* out_len) { int key_size = RSA_size(rsa); int max_block = key_size - 11; int blocks = (in_len + max_block - 1) / max_block; *out = malloc(key_size * blocks); if (!*out) return 0; unsigned char* out_ptr = *out; const unsigned char* in_ptr = in; int remaining = in_len; while (remaining > 0) { int curr_size = (remaining > max_block) ? max_block : remaining; int ret = RSA_public_encrypt(curr_size, in_ptr, out_ptr, rsa, RSA_PKCS1_PADDING); if (ret != key_size) { free(*out); *out = NULL; return 0; } in_ptr += curr_size; out_ptr += key_size; remaining -= curr_size; } *out_len = key_size * blocks; return 1; }这个改进版本:
- 使用指针算术而非数组索引,效率更高
- 更清晰地跟踪剩余数据量
- 错误处理时确保不会返回悬空指针
3.2 性能优化技巧
对于大数据量的加密,我们可以考虑以下优化:
- 预计算密钥大小:避免每次循环调用
RSA_size - 批量分配内存:一次性分配足够空间而非逐块分配
- 并行处理:对独立的数据块使用多线程加密
// 线程安全版本的RSA加密包装函数 void* rsa_encrypt_thread(void* arg) { ThreadData* data = (ThreadData*)arg; >int rsa_sign(RSA* rsa, const unsigned char* data, int data_len, unsigned char** sig, int* sig_len) { // 计算SHA-256哈希 unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(data, data_len, hash); // 分配签名缓冲区 *sig_len = RSA_size(rsa); *sig = malloc(*sig_len); if (!*sig) return 0; // 执行签名 unsigned int sig_len_u; if (!RSA_sign(NID_sha256, hash, SHA256_DIGEST_LENGTH, *sig, &sig_len_u, rsa)) { free(*sig); return 0; } *sig_len = sig_len_u; return 1; } int rsa_verify(RSA* rsa, const unsigned char* data, int data_len, const unsigned char* sig, int sig_len) { // 计算SHA-256哈希 unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(data, data_len, hash); // 验证签名 return RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, sig, sig_len, rsa) == 1; }4.2 签名验证的常见陷阱
在实际项目中,签名验证容易犯以下错误:
- 哈希算法不匹配:签名和验证使用不同的哈希算法
- 返回值误解:
RSA_verify返回1表示成功,0表示失败,而非非零即成功 - 内存边界问题:未检查输入签名长度是否匹配密钥大小
- 时序攻击:简单的比较操作可能泄露验证信息
改进后的安全验证函数:
int rsa_verify_safe(RSA* rsa, const unsigned char* data, int data_len, const unsigned char* sig, int sig_len) { if (sig_len != RSA_size(rsa)) { return 0; } unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(data, data_len, hash); unsigned char* tmp_sig = malloc(sig_len); if (!tmp_sig) return 0; // 防止时序攻击:无论结果如何都执行完整验证 int ret = RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, sig, sig_len, rsa); int ret_dummy = RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, tmp_sig, sig_len, rsa); free(tmp_sig); return ret == 1; }5. 完整项目示例
5.1 项目结构设计
一个健壮的RSA加密项目应该包含以下模块:
rsa_crypto/ ├── include/ │ ├── rsa_util.h // 核心功能声明 │ └── crypto_utils.h // 辅助工具函数 ├── src/ │ ├── rsa_util.c // RSA实现 │ ├── crypto_utils.c // 辅助工具实现 │ └── main.c // 测试示例 ├── tests/ // 单元测试 └── Makefile // 构建配置5.2 核心实现代码
rsa_util.h关键定义:
#ifndef RSA_UTIL_H #define RSA_UTIL_H #include <openssl/rsa.h> #include <stddef.h> typedef struct { unsigned char* data; size_t length; } Buffer; Buffer rsa_encrypt_buffer(RSA* pub_key, const Buffer* input); Buffer rsa_decrypt_buffer(RSA* priv_key, const Buffer* input); Buffer rsa_sign_data(RSA* priv_key, const Buffer* data); int rsa_verify_signature(RSA* pub_key, const Buffer* data, const Buffer* signature); void free_buffer(Buffer* buf); #endifrsa_util.c核心实现:
#include "rsa_util.h" #include <openssl/err.h> #include <string.h> Buffer rsa_encrypt_buffer(RSA* pub_key, const Buffer* input) { Buffer output = {NULL, 0}; if (!pub_key || !input || !input->data || input->length == 0) { return output; } int rsa_size = RSA_size(pub_key); int max_block = rsa_size - 11; int blocks = (input->length + max_block - 1) / max_block; output.data = malloc(rsa_size * blocks); if (!output.data) { return output; } unsigned char* out_ptr = output.data; const unsigned char* in_ptr = input->data; size_t remaining = input->length; int success = 1; while (remaining > 0) { int curr_size = (remaining > max_block) ? max_block : remaining; int ret = RSA_public_encrypt(curr_size, in_ptr, out_ptr, pub_key, RSA_PKCS1_PADDING); if (ret != rsa_size) { success = 0; break; } in_ptr += curr_size; out_ptr += rsa_size; remaining -= curr_size; } if (!success) { free(output.data); output.data = NULL; output.length = 0; } else { output.length = rsa_size * blocks; } return output; } Buffer rsa_sign_data(RSA* priv_key, const Buffer* data) { Buffer signature = {NULL, 0}; if (!priv_key || !data || !data->data ||>#include "rsa_util.h" #include <stdio.h> #include <time.h> void test_rsa_roundtrip() { // 生成测试密钥对 RSA* keypair = RSA_generate_key(2048, RSA_F4, NULL, NULL); if (!keypair) { fprintf(stderr, "Failed to generate RSA key pair\n"); return; } // 准备测试数据 (1MB + 3 bytes) size_t test_size = 1*1024*1024 + 3; Buffer test_data = {malloc(test_size), test_size}; for (size_t i = 0; i < test_size; i++) { test_data.data[i] = i % 256; } clock_t start = clock(); // 加密测试 Buffer encrypted = rsa_encrypt_buffer(keypair, &test_data); if (!encrypted.data) { fprintf(stderr, "Encryption failed\n"); goto cleanup; } // 解密测试 Buffer decrypted = rsa_decrypt_buffer(keypair, &encrypted); if (!decrypted.data) { fprintf(stderr, "Decryption failed\n"); goto cleanup; } // 验证数据一致性 if (decrypted.length != test_data.length || memcmp(decrypted.data, test_data.data, decrypted.length) != 0) { fprintf(stderr, "Data mismatch after roundtrip\n"); } else { printf("Roundtrip test passed!\n"); } // 签名验证测试 Buffer signature = rsa_sign_data(keypair, &test_data); if (!signature.data) { fprintf(stderr, "Signing failed\n"); goto cleanup; } if (rsa_verify_signature(keypair, &test_data, &signature)) { printf("Signature verification passed!\n"); } else { fprintf(stderr, "Signature verification failed\n"); } clock_t end = clock(); printf("Operation took %.2f seconds\n", (double)(end - start) / CLOCKS_PER_SEC); cleanup: free_buffer(&test_data); free_buffer(&encrypted); free_buffer(&decrypted); free_buffer(&signature); RSA_free(keypair); } int main() { // 初始化OpenSSL OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); test_rsa_roundtrip(); // 清理OpenSSL EVP_cleanup(); ERR_free_strings(); return 0; }6. 工程实践中的经验分享
在实际项目中集成OpenSSL RSA功能时,有几个经验教训值得分享:
内存管理:每个
RSA_new()或PEM_read调用都需要对应的RSA_free,OpenSSL不会自动释放这些资源。错误处理:OpenSSL错误堆栈需要显式处理,否则错误信息会被丢弃:
void print_openssl_error() { unsigned long err = ERR_get_error(); if (err) { fprintf(stderr, "OpenSSL error: %s\n", ERR_error_string(err, NULL)); } }- 线程安全:虽然OpenSSL现在默认是线程安全的,但在旧版本中需要显式初始化:
#include <openssl/crypto.h> void init_openssl_thread_safety() { CRYPTO_thread_setup(); // ... 应用代码 ... CRYPTO_thread_cleanup(); }- 性能监控:RSA操作可能成为性能瓶颈,特别是在嵌入式系统中。建议添加性能统计:
#include <sys/time.h> struct timeval start, end; gettimeofday(&start, NULL); // RSA操作... gettimeofday(&end, NULL); long seconds = end.tv_sec - start.tv_sec; long micros = ((seconds * 1000000) + end.tv_usec) - start.tv_usec; printf("Operation took %ld microseconds\n", micros);- 密钥安全:私钥在内存中的处理要特别小心,避免被交换到磁盘或通过核心转储泄露:
#include <sys/mman.h> void secure_buffer(Buffer* buf) { if (buf->data && buf->length > 0) { mlock(buf->data, buf->length); // 锁定内存 madvise(buf->data, buf->length, MADV_DONTDUMP); // 避免核心转储 } }7. 跨平台兼容性处理
不同平台对OpenSSL的支持有所差异,以下是几个常见问题的解决方案:
- Windows链接问题:
// 在Windows上需要显式链接OpenSSL的导入库 #ifdef _WIN32 #pragma comment(lib, "libcrypto.lib") #pragma comment(lib, "libssl.lib") #endif- Android NDK集成:
# Android.mk 配置示例 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := crypto LOCAL_SRC_FILES := prebuilt/libcrypto.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := ssl LOCAL_SRC_FILES := prebuilt/libssl.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := my_crypto_app LOCAL_SRC_FILES := my_crypto_code.c LOCAL_STATIC_LIBRARIES := ssl crypto include $(BUILD_SHARED_LIBRARY)- iOS安全注意事项:
// 在iOS上使用Keychain存储密钥更安全 - (void)storePrivateKeyInKeychain:(RSA *)rsa { NSData *privateKeyData = [self dataFromRSA:rsa]; NSDictionary *query = @{ (id)kSecClass: (id)kSecClassKey, (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA, (id)kSecAttrApplicationTag: @"com.example.privatekey", (id)kSecValueData: privateKeyData, (id)kSecAttrIsPermanent: @YES, (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly }; SecItemDelete((CFDictionaryRef)query); OSStatus status = SecItemAdd((CFDictionaryRef)query, NULL); if (status != errSecSuccess) { NSLog(@"Failed to store private key in Keychain"); } }8. 安全加固建议
在生产环境中使用RSA加密时,还需要考虑以下安全措施:
- 密钥轮换:定期更换密钥对,即使旧密钥未被破解
void rotate_keys(RSA** old_key) { RSA* new_key = RSA_generate_key(2048, RSA_F4, NULL, NULL); if (new_key) { RSA_free(*old_key); *old_key = new_key; } }- 填充方案选择:考虑使用更安全的OAEP填充而非PKCS#1 v1.5
int rsa_encrypt_oaep(RSA* rsa, const unsigned char* in, int in_len, unsigned char** out) { int rsa_size = RSA_size(rsa); *out = malloc(rsa_size); if (!*out) return 0; return RSA_public_encrypt(in_len, in, *out, rsa, RSA_PKCS1_OAEP_PADDING); }- 侧信道攻击防护:确保代码不会通过时序或功耗泄露信息
int secure_memcmp(const void* a, const void* b, size_t len) { const unsigned char* pa = a; const unsigned char* pb = b; unsigned char result = 0; for (size_t i = 0; i < len; i++) { result |= pa[i] ^ pb[i]; } return result; }- 内存清理:敏感数据使用后应立即从内存中清除
void secure_free(void* ptr, size_t len) { if (ptr) { memset(ptr, 0, len); free(ptr); } }9. 调试与问题排查
当RSA操作失败时,系统化的排查方法能节省大量时间:
- 错误代码获取:
void log_openssl_errors() { unsigned long err; while ((err = ERR_get_error())) { char* str = ERR_error_string(err, NULL); fprintf(stderr, "OpenSSL error: %s\n", str); } }- 密钥有效性检查:
int validate_rsa_key(RSA* rsa) { if (!rsa) return 0; // 检查模数是否存在 if (!rsa->n || BN_num_bits(rsa->n) < 512) { fprintf(stderr, "Invalid RSA modulus\n"); return 0; } // 简单验证密钥一致性 if (RSA_check_key(rsa) != 1) { fprintf(stderr, "RSA key validation failed\n"); return 0; } return 1; }- 输入输出验证:
void dump_hex(const char* label, const unsigned char* data, int len) { printf("%s (%d bytes):\n", label, len); for (int i = 0; i < len; i++) { printf("%02x ", data[i]); if ((i+1) % 16 == 0) printf("\n"); } printf("\n"); }- 性能分析工具:
# 使用Valgrind检查内存问题 valgrind --leak-check=full ./rsa_test # 使用gprof分析性能热点 gcc -pg -o rsa_test rsa_util.c main.c -lcrypto ./rsa_test gprof rsa_test gmon.out > analysis.txt10. 现代替代方案评估
虽然RSA仍然广泛使用,但在新项目中可以考虑以下替代方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ECC | 更短的密钥长度,更高的安全性 | 实现复杂,专利问题 | IoT设备,移动应用 |
| Ed25519 | 高性能,高安全性 | 兼容性较差 | 新系统,内部应用 |
| PQ Crypto | 抗量子计算 | 不成熟,性能差 | 未来保障系统 |
迁移到ECC的示例:
#include <openssl/ec.h> #include <openssl/ecdsa.h> EC_KEY* generate_ecc_key() { EC_KEY* key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (!key) return NULL; if (!EC_KEY_generate_key(key)) { EC_KEY_free(key); return NULL; } return key; } int ecdsa_sign(EC_KEY* key, const unsigned char* data, size_t len, unsigned char** sig, size_t* sig_len) { *sig = malloc(ECDSA_size(key)); if (!*sig) return 0; return ECDSA_sign(0, data, len, *sig, (unsigned int*)sig_len, key); }在实际项目中,RSA与ECC的选择应该基于:
- 目标平台的支持情况
- 安全要求级别
- 性能需求
- 兼容性要求
11. 项目构建与集成
将RSA功能集成到现有项目时,构建系统的配置也很关键:
CMake配置示例:
cmake_minimum_required(VERSION 3.10) project(rsa_crypto) find_package(OpenSSL REQUIRED) add_executable(rsa_test src/rsa_util.c src/main.c ) target_include_directories(rsa_test PRIVATE include) target_link_libraries(rsa_test OpenSSL::Crypto) if(WIN32) target_compile_definitions(rsa_test PRIVATE WIN32_LEAN_AND_MEAN) endif()Makefile示例:
CC = gcc CFLAGS = -Wall -O2 -Iinclude LDFLAGS = -lcrypto SRC = src/rsa_util.c src/main.c OBJ = $(SRC:.c=.o) TARGET = rsa_test all: $(TARGET) $(TARGET): $(OBJ) $(CC) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< clean: rm -f $(OBJ) $(TARGET)12. 持续集成与测试
自动化测试对密码学代码尤为重要,以下是测试金字塔的实现:
- 单元测试:验证每个独立函数
// 使用Check单元测试框架 #include <check.h> START_TEST(test_rsa_encrypt_decrypt) { RSA* key = RSA_generate_key(2048, RSA_F4, NULL, NULL); ck_assert_ptr_nonnull(key); const char* test_str = "Hello, RSA!"; Buffer input = {(unsigned char*)test_str, strlen(test_str)}; Buffer encrypted = rsa_encrypt_buffer(key, &input); ck_assert_ptr_nonnull(encrypted.data); Buffer decrypted = rsa_decrypt_buffer(key, &encrypted); ck_assert_ptr_nonnull(decrypted.data); ck_assert_int_eq(decrypted.length, input.length); ck_assert_int_eq(memcmp(decrypted.data, input.data, decrypted.length), 0); free_buffer(&encrypted); free_buffer(&decrypted); RSA_free(key); } END_TEST- 集成测试:验证多个组件的协作
START_TEST(test_rsa_sign_verify) { RSA* key = RSA_generate_key(2048, RSA_F4, NULL, NULL); ck_assert_ptr_nonnull(key); const char* test_str = "Important data to sign"; Buffer data = {(unsigned char*)test_str, strlen(test_str)}; Buffer signature = rsa_sign_data(key, &data); ck_assert_ptr_nonnull(signature.data); int verified = rsa_verify_signature(key, &data, &signature); ck_assert_int_eq(verified, 1); // 篡改数据后验证应失败 data.data[0] ^= 0xFF; verified = rsa_verify_signature(key, &data, &signature); ck_assert_int_eq(verified, 0); free_buffer(&signature); RSA_free(key); } END_TEST- 性能测试:确保满足性能要求
#include <time.h> void run_performance_test() { RSA* key = RSA_generate_key(2048, RSA_F4, NULL, NULL); if (!key) return; // 准备1MB测试数据 size_t data_size = 1024*1024; unsigned char* data = malloc(data_size); memset(data, 0xAA, data_size); Buffer input = {data, data_size}; clock_t start = clock(); Buffer encrypted = rsa_encrypt_buffer(key, &input); if (!encrypted.data) goto cleanup; Buffer decrypted = rsa_decrypt_buffer(key, &encrypted); if (!decrypted.data) goto cleanup; clock_t end = clock(); double elapsed = (double)(end - start) / CLOCKS_PER_SEC; printf("Encrypt+decrypt 1MB took %.2f seconds (%.2f MB/s)\n", elapsed, 1.0 / elapsed); cleanup: free_buffer(&encrypted); free_buffer(&decrypted); free(data); RSA_free(key); }13. 文档与API设计
良好的API设计能大大降低集成难度:
- 清晰的头文件注释:
/** * @brief 使用RSA公钥加密数据 * * @param rsa 已初始化的RSA公钥结构体 * @param in 输入数据缓冲区 * @param in_len 输入数据长度 * @param out 输出缓冲区指针,将由函数分配内存 * @param out_len 输出数据长度 * @return int 成功返回1,失败返回0 * * @note 调用者负责释放out缓冲区内存 * @warning 输入数据长度不能超过RSA密钥长度限制 */ int rsa_encrypt(RSA* rsa, const unsigned char* in, int in_len, unsigned char** out, int* out_len);- 示例代码片段:
/* // 示例:RSA加密解密流程 RSA* key = load_public_key("public.pem"); if (!key) handle_error(); unsigned char plaintext[] = "Secret message"; unsigned char* ciphertext = NULL; int ciphertext_len = 0; if (!rsa_encrypt(key, plaintext, strlen(plaintext), &ciphertext, &ciphertext_len)) { handle_error(); } // ...传输或存储密文... unsigned char* decrypted = NULL; int decrypted_len = 0; if (!rsa_decrypt(key, ciphertext, ciphertext_len, &decrypted, &decrypted_len)) { handle_error(); } printf("Decrypted: %.*s\n", decrypted_len, decrypted); free(ciphertext); free(decrypted); RSA_free(key); */- Doxygen文档生成:
/** * @file rsa_util.h * @brief RSA加密解密工具集 * * 提供完整的RSA加密、解密、签名和验证功能实现, * 支持大数据分段处理和内存安全操作。 */ PROJECT_NAME = "RSA Crypto Library" OUTPUT_DIRECTORY = docs GENERATE_LATEX = NO GENERATE_HTML = YES INPUT = include src FILE_PATTERNS = *.h *.c RECURSIVE = YES14. 兼容性与版本控制
处理不同OpenSSL版本的兼容性问题:
- 版本检测:
#include <openssl/opensslv.h> void check_openssl_version() { printf("OpenSSL version: %s\n", OpenSSL_version(SSLEAY_VERSION)); #if OPENSSL_VERSION_NUMBER < 0x10100000L printf("Warning: Using legacy OpenSSL version (< 1.1.0)\n"); #endif }- API兼容层:
#if OPENSSL_VERSION_NUMBER < 0x10100000L // 兼容旧版OpenSSL的RSA_new方法 static RSA* RSA_new_impl(void) { RSA* rsa = RSA_new(); if (rsa) { rsa->flags |= RSA_FLAG_NO_BLINDING; } return rsa; } #else // 新版OpenSSL已经处理了这个问题 #define RSA_new_impl RSA_new #endif- 功能检测:
int has_rsa_oaep() { #if defined(RSA_PKCS1_OAEP_PADDING) return 1; #else return 0; #endif }15. 资源清理模式
确保在任何情况下都正确释放资源:
- Goto清理模式:
int rsa_operation() { RSA* rsa = NULL; unsigned char* buf1 = NULL; unsigned char* buf2 = NULL; rsa = RSA_new(); if (!rsa) goto cleanup; buf1 = malloc(1024); if (!buf1) goto cleanup; buf2 = malloc(2048); if (!buf2) goto cleanup; // 主要操作逻辑 // ... int result = 1; cleanup: if (rsa) RSA_free(rsa); if (buf1) free(buf1); if (buf2) free(buf2); return result; }- RAII风格包装:
typedef struct { RSA* rsa; unsigned char* buffer; } RsaContext; void cleanup_rsa_context(RsaContext* ctx) { if (ctx->rsa) RSA_free(ctx->rsa); if (ctx->buffer) free(ctx->buffer); memset(ctx, 0