请求提供OpenSSL 3.0 C++生成RSA密钥对的可用示例代码
OpenSSL 3.0 C++ 生成RSA密钥对的正确示例
用户需要一个使用OpenSSL 3.0 C++库生成RSA密钥对的简单示例,官方文档质量不佳,大量废弃函数仍被列出,且GPT生成的代码在EVP_PKEY_copy_parameters调用处执行失败。以下是GPT生成的无效代码:
#include <iostream> #include <memory> #include <openssl/err.h> #include <openssl/pem.h> #include <openssl/rsa.h> #include <string> #include <vector> // Custom deleter for EVP_PKEY struct EVP_PKEY_Deleter { void operator()(EVP_PKEY *pkey) const { EVP_PKEY_free(pkey); } }; // Type alias for unique_ptr with EVP_PKEY and custom deleter using Key = std::unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>; std::pair<Key, Key> generate_rsa_keys() { EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); if (!ctx) { throw std::runtime_error("Failed to create context"); } EVP_PKEY *pkey_raw = EVP_RSA_gen(2048); if (!pkey_raw) { EVP_PKEY_CTX_free(ctx); throw std::runtime_error("Key generation failed"); } EVP_PKEY *public_key_raw = EVP_PKEY_new(); if (!public_key_raw) { EVP_PKEY_free(pkey_raw); EVP_PKEY_CTX_free(ctx); throw std::runtime_error("Failed to create public key"); } if (EVP_PKEY_copy_parameters(public_key_raw, pkey_raw) <= 0) { EVP_PKEY_free(pkey_raw); EVP_PKEY_free(public_key_raw); EVP_PKEY_CTX_free(ctx); throw std::runtime_error("Failed to copy parameters"); } Key pkey(pkey_raw); Key public_key(public_key_raw); return {std::move(pkey), std::move(public_key)}; } int main() { auto [pkey, public_key] = generate_rsa_keys(); }
原代码问题分析
- API版本过时:
EVP_RSA_gen是OpenSSL旧版密钥生成API,OpenSSL 3.0中推荐使用更通用的EVP_PKEY_keygen系列接口,该接口支持多种密钥类型,且符合新版本的设计规范。 - 公钥提取错误:
EVP_PKEY_copy_parameters仅复制密钥的算法参数(如RSA的模数长度),并不会提取公钥内容,因此无法生成有效的公钥对象。
正确实现示例
以下是符合OpenSSL 3.0规范的RSA密钥对生成代码,包含完整的错误处理和公钥提取逻辑:
#include <iostream> #include <memory> #include <stdexcept> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/pem.h> // 自定义智能指针删除器 struct EVP_PKEY_Deleter { void operator()(EVP_PKEY* pkey) const { if (pkey) EVP_PKEY_free(pkey); } }; struct EVP_PKEY_CTX_Deleter { void operator()(EVP_PKEY_CTX* ctx) const { if (ctx) EVP_PKEY_CTX_free(ctx); } }; using UniquePKEY = std::unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>; using UniquePKEYCTX = std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Deleter>; // 打印OpenSSL错误信息 void print_openssl_errors() { ERR_print_errors_fp(stderr); } std::pair<UniquePKEY, UniquePKEY> generate_rsa_keys(int key_size = 2048) { // 创建密钥生成上下文 UniquePKEYCTX ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr)); if (!ctx) { print_openssl_errors(); throw std::runtime_error("Failed to create EVP_PKEY context"); } // 初始化密钥生成操作 if (EVP_PKEY_keygen_init(ctx.get()) <= 0) { print_openssl_errors(); throw std::runtime_error("Failed to initialize key generation"); } // 设置RSA密钥长度 if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), key_size) <= 0) { print_openssl_errors(); throw std::runtime_error("Failed to set RSA key size"); } // 生成密钥对(私钥) EVP_PKEY* pkey_raw = nullptr; if (EVP_PKEY_keygen(ctx.get(), &pkey_raw) <= 0) { print_openssl_errors(); throw std::runtime_error("RSA key generation failed"); } UniquePKEY private_key(pkey_raw); // 从私钥中提取公钥 EVP_PKEY* pubkey_raw = EVP_PKEY_get1_public_key(private_key.get()); if (!pubkey_raw) { print_openssl_errors(); throw std::runtime_error("Failed to extract public key"); } UniquePKEY public_key(pubkey_raw); return {std::move(private_key), std::move(public_key)}; } // 可选:将密钥保存为PEM格式 void save_key_to_pem(const EVP_PKEY* key, const std::string& filename, bool is_private) { FILE* fp = fopen(filename.c_str(), "wb"); if (!fp) { throw std::runtime_error("Failed to open file for writing"); } int result = 0; if (is_private) { result = PEM_write_PrivateKey(fp, const_cast<EVP_PKEY*>(key), nullptr, nullptr, 0, nullptr, nullptr); } else { result = PEM_write_PUBKEY(fp, const_cast<EVP_PKEY*>(key)); } fclose(fp); if (!result) { print_openssl_errors(); throw std::runtime_error("Failed to write key to PEM file"); } } int main() { // 初始化OpenSSL错误处理 ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); try { auto [private_key, public_key] = generate_rsa_keys(2048); std::cout << "RSA key pair generated successfully\n"; // 可选:保存密钥到文件 save_key_to_pem(private_key.get(), "private_key.pem", true); save_key_to_pem(public_key.get(), "public_key.pem", false); std::cout << "Keys saved to private_key.pem and public_key.pem\n"; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << "\n"; return 1; } // 清理OpenSSL资源 EVP_cleanup(); ERR_free_strings(); return 0; }
补充说明
- 代码使用智能指针管理OpenSSL对象,避免手动内存泄漏
- 通过
EVP_PKEY_get1_public_key正确从私钥中提取公钥,这是OpenSSL 3.0中推荐的公钥获取方式 - 包含错误信息打印函数,便于调试
- 可选的PEM格式保存功能,方便后续使用
新手也可以考虑使用封装好的C++加密库来简化操作,避免直接处理OpenSSL的复杂底层API。
内容的提问来源于stack exchange,提问作者X. Sun
相关产品推荐
相关产品推荐

