将EVP_PKEY类型改为EVP_PKEY_RSA_PSS导致C程序段错误
问题分析与解决方案
问题根源
直接调用EVP_PKEY_set_type将密钥类型改为EVP_PKEY_RSA_PSS会触发段错误,核心原因是:EVP_PKEY_RSA仅包含RSA核心参数(模n、指数e),而EVP_PKEY_RSA_PSS属于带特定配置的RSA变种,需要额外的PSS参数(如MGF1哈希算法、盐长度、签名哈希算法等)。直接修改类型后,密钥结构缺失这些必要参数,后续函数(如PEM_write_PUBKEY)访问不存在的内存时就会崩溃。
正确实现方式
要生成合法的EVP_PKEY_RSA_PSS类型公钥,需在构建密钥时明确初始化PSS相关参数,而非直接修改已有RSA密钥的类型。以下是修正后的代码:
#include <openssl/evp.h> #include <openssl/rsa.h> #include <openssl/bn.h> #include <stdio.h> // 假设bignum_base64_decode是已实现的Base64URL解码函数 BIGNUM* bignum_base64_decode(const char* data); int generate_pubkey(char* exponent, char* modulus, EVP_PKEY** pubkey) { BIGNUM *n = bignum_base64_decode(modulus); BIGNUM *e = bignum_base64_decode(exponent); int ret = -1; if (!e || !n) { printf("Error: Invalid encoding for public exponent or modulus\n"); goto cleanup; } // 1. 创建普通RSA结构并设置核心参数 RSA* rsa = RSA_new(); if (!rsa || !RSA_set0_key(rsa, n, e, NULL)) { printf("Error: Failed to set RSA key components\n"); goto cleanup; } // 注:RSA_set0_key会接管n和e的所有权,无需手动释放 // 2. 创建RSA-PSS类型的EVP_PKEY上下文 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL); if (!ctx) { printf("Error: Failed to create RSA-PSS context\n"); goto cleanup_rsa; } // 3. 初始化上下文并设置PSS参数(这里使用SHA256,可按需修改) if (EVP_PKEY_fromdata_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0 || // 盐长度等于哈希长度 EVP_PKEY_CTX_set_md(ctx, EVP_sha256()) <= 0) { printf("Error: Failed to configure RSA-PSS parameters\n"); goto cleanup_ctx; } // 4. 从RSA结构生成RSA-PSS类型的EVP_PKEY if (EVP_PKEY_fromdata(ctx, pubkey, EVP_PKEY_PUBLIC_KEY, rsa) <= 0) { printf("Error: Failed to create RSA-PSS public key\n"); goto cleanup_ctx; } // 5. 写入PEM文件 FILE* fp = fopen("pubnew.key", "w"); if (!fp) { printf("Failed to open file for writing\n"); goto cleanup_pubkey; } if (PEM_write_PUBKEY(fp, *pubkey) <= 0) { printf("Error: Failed to write public key to file\n"); } else { ret = 0; } fclose(fp); cleanup_pubkey: // 写入失败时释放生成的公钥 if (ret != 0 && *pubkey) { EVP_PKEY_free(*pubkey); *pubkey = NULL; } cleanup_ctx: EVP_PKEY_CTX_free(ctx); cleanup_rsa: RSA_free(rsa); // RSA_free会自动释放n和e return ret; cleanup: if (n) BN_free(n); if (e) BN_free(e); return ret; }
关键说明
- 参数完整性:必须通过
EVP_PKEY_CTX配置RSA-PSS的必要参数,否则生成的密钥结构不合法,无法正常使用。 - 内存所有权:
RSA_set0_key会接管n和e的内存,EVP_PKEY_fromdata会接管rsa的内存,无需重复释放。 - 错误处理:所有OpenSSL函数调用都需检查返回值,避免内存泄漏或非法内存访问。
内容的提问来源于stack exchange,提问作者Vijay
相关产品推荐
相关产品推荐

