当前位置: 首页 > news >正文

从原理到代码:手把手教你用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; }

这个基础框架已经解决了几个关键问题:

  1. 自动计算需要的分段数量
  2. 合理分配输出缓冲区
  3. 处理最后一块可能不足的情况
  4. 错误处理和内存释放

2. OpenSSL RSA函数深度解析

2.1 核心API的工程考量

OpenSSL的RSA API看似简单,但实际使用中有许多细节需要注意:

函数关键参数常见陷阱最佳实践
RSA_public_encryptflen, padding输入长度限制检查返回值是否等于密钥长度
RSA_private_decryptflen, padding内存对齐问题输出缓冲区预留RSA_size(rsa)空间
RSA_signtype, m_len哈希算法匹配确保使用相同的哈希算法验签
RSA_verifytype, 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; }

这个改进版本:

  1. 使用指针算术而非数组索引,效率更高
  2. 更清晰地跟踪剩余数据量
  3. 错误处理时确保不会返回悬空指针

3.2 性能优化技巧

对于大数据量的加密,我们可以考虑以下优化:

  1. 预计算密钥大小:避免每次循环调用RSA_size
  2. 批量分配内存:一次性分配足够空间而非逐块分配
  3. 并行处理:对独立的数据块使用多线程加密
// 线程安全版本的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 签名验证的常见陷阱

在实际项目中,签名验证容易犯以下错误:

  1. 哈希算法不匹配:签名和验证使用不同的哈希算法
  2. 返回值误解RSA_verify返回1表示成功,0表示失败,而非非零即成功
  3. 内存边界问题:未检查输入签名长度是否匹配密钥大小
  4. 时序攻击:简单的比较操作可能泄露验证信息

改进后的安全验证函数:

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); #endif

rsa_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功能时,有几个经验教训值得分享:

  1. 内存管理:每个RSA_new()PEM_read调用都需要对应的RSA_free,OpenSSL不会自动释放这些资源。

  2. 错误处理:OpenSSL错误堆栈需要显式处理,否则错误信息会被丢弃:

void print_openssl_error() { unsigned long err = ERR_get_error(); if (err) { fprintf(stderr, "OpenSSL error: %s\n", ERR_error_string(err, NULL)); } }
  1. 线程安全:虽然OpenSSL现在默认是线程安全的,但在旧版本中需要显式初始化:
#include <openssl/crypto.h> void init_openssl_thread_safety() { CRYPTO_thread_setup(); // ... 应用代码 ... CRYPTO_thread_cleanup(); }
  1. 性能监控: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);
  1. 密钥安全:私钥在内存中的处理要特别小心,避免被交换到磁盘或通过核心转储泄露:
#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的支持有所差异,以下是几个常见问题的解决方案:

  1. Windows链接问题
// 在Windows上需要显式链接OpenSSL的导入库 #ifdef _WIN32 #pragma comment(lib, "libcrypto.lib") #pragma comment(lib, "libssl.lib") #endif
  1. 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)
  1. 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加密时,还需要考虑以下安全措施:

  1. 密钥轮换:定期更换密钥对,即使旧密钥未被破解
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; } }
  1. 填充方案选择:考虑使用更安全的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); }
  1. 侧信道攻击防护:确保代码不会通过时序或功耗泄露信息
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; }
  1. 内存清理:敏感数据使用后应立即从内存中清除
void secure_free(void* ptr, size_t len) { if (ptr) { memset(ptr, 0, len); free(ptr); } }

9. 调试与问题排查

当RSA操作失败时,系统化的排查方法能节省大量时间:

  1. 错误代码获取
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); } }
  1. 密钥有效性检查
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; }
  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"); }
  1. 性能分析工具
# 使用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.txt

10. 现代替代方案评估

虽然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. 持续集成与测试

自动化测试对密码学代码尤为重要,以下是测试金字塔的实现:

  1. 单元测试:验证每个独立函数
// 使用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
  1. 集成测试:验证多个组件的协作
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
  1. 性能测试:确保满足性能要求
#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设计能大大降低集成难度:

  1. 清晰的头文件注释
/** * @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);
  1. 示例代码片段
/* // 示例: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); */
  1. 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 = YES

14. 兼容性与版本控制

处理不同OpenSSL版本的兼容性问题:

  1. 版本检测
#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 }
  1. 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
  1. 功能检测
int has_rsa_oaep() { #if defined(RSA_PKCS1_OAEP_PADDING) return 1; #else return 0; #endif }

15. 资源清理模式

确保在任何情况下都正确释放资源:

  1. 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; }
  1. 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
http://www.jsqmd.com/news/663500/

相关文章:

  • ABR 会将自身所在区域内的路由(包括直连网段)通过 Type 3 LSA 通告到其他区域,但不会通告回本区域
  • Multi-Agent产品策略:从功能堆砌到智能工作流的重构
  • MT7916芯片深度解析:从拆机中兴E1630看MTK首款AX3000方案
  • Zotero-OCR插件:3步实现PDF文献智能识别与可搜索文本层添加
  • 【雷达成像】基于二维ADMM的稀度驱动ISAR成像附Matlab复现含文献
  • X.509数字证书实战解析:从结构到应用
  • 别再只读SOC了!MAX17048电量计的高级玩法:休眠管理、报警阈值设置与电量跳变修复
  • MATLAB条形图进阶:从基础bar函数到数据可视化实战
  • RobotStudio导入外部工具模型避坑指南:从‘无坐标’模型到可用的工具坐标系
  • Databricks 自定义容器配置指南
  • 从PID调参到根轨迹:一个电机控制工程师的实战避坑笔记
  • STM32 HAL库SPI驱动ST7789中景园屏实战:从CubeMX配置到显示优化
  • d2s-editor:暗黑破坏神2存档编辑实战指南与深度解析
  • 信息学奥赛一本通 1248:Dungeon Master | 三维迷宫搜索算法精讲
  • 别再手动算面积和距离了!用Shapely处理GeoJSON数据,效率提升10倍
  • 基于西门子PLCS7-1200的程序仿真立体车库设计报告(含硬件原理图和CAD)
  • AI大模型对内容创作的颠覆:机遇、版权争议与行业新规则
  • MIPI-DSI协议解析:从物理层到应用层的LCD驱动实践
  • 深度学习---注意力机制(Attention Mechanism)
  • 别再复制粘贴了!手把手教你用原生Canvas实现一个会呼吸的六边形能力图(附完整源码)
  • 移动零题解
  • 神经网络参数初始化:从梯度失控到模型收敛的核心密码
  • 【红队利器】Ehole实战指南:从指纹识别到精准打击
  • 如何完整解锁ComfyUI-Impact-Pack V8版的所有图像增强功能
  • 从源码到实战:手把手教你编译与定制化iperf网络性能测试工具
  • FanControl完全指南:5分钟掌握Windows风扇精准控制,告别电脑噪音烦恼
  • 【实战指南】【驱动解析】SSD1306 OLED屏I2C/SPI接口初始化与核心指令详解
  • GitHub Copilot v4 vs. CodeWhisperer v3 vs. Tabnine Enterprise(2024Q2实测对比:函数级生成稳定性TOP3排名揭晓)
  • 告别复制粘贴!用Keil5为GD32F4xx搭建标准工程模板(附文件清单与一键清理脚本)
  • 蓝桥杯单片机实战:PCF8591的A/D与D/A协同编程与常见驱动陷阱解析