VC++ 使用OpenSSL创建RSA密钥PEM档案

VC++ 使用OpenSSL创建RSA密钥PEM文件

某个桌面应用需要对一些文件进行签名处理,上一篇写了C#相关的一些签名与验签,因原来的历史代码是VC++开发的,也许考虑在上面做类似的工作。
目前是Vibe Coding的时代了,AI这样的助手显然也派上了用场,在AI配合下,我把开发的内容给大家分享一下。
生成的私钥截图

准备工作

1. 安装 OpenSSL

  • Windows: 可以从 OpenSSL 的官方网站或第三方网站(如 https://slproweb.com/products/Win32OpenSSL.html)下载预编译的二进制文件。安装后,记下安装路径。
  • Linux/macOS: 通常通过包管理器安装:
    • Debian/Ubuntu:sudo apt-get install libssl-dev
    • CentOS/Fedora:sudo yum install openssl-devel
    • macOS (Homebrew):brew install openssl

我的这个项目因为是用了vcpkg进行包管理(之前我的博文:VCPKG配合NuGet在项目中使用包也有过简单的描述),直接用vcpkg安装并按照开源项目的方式给MSBUILD使用就行了。参考命令如下:

vcpkg install openssl:x64-windows
vcpkg integrate install

2. Visual Studio 项目配置 (Windows)

如果你在Windows上使用Visual Studio,你需要配置项目的 “附加包含目录” 和 “附加库目录” 以及 “附加依赖项”。

  • C/C++ -> 附加包含目录:
    • C:\OpenSSL-Win64\include (替换为你的OpenSSL安装路径)
  • 链接器 -> 附加库目录:
    • C:\OpenSSL-Win64\lib (替换为你的OpenSSL安装路径)
  • 链接器 -> 输入 -> 附加依赖项:
    • libssl.lib (或 ssleay32.lib)
    • libcrypto.lib (或 libeay32.lib)
    • ws2_32.lib (用于网络功能,但在这里可能不需要,最好加上以防万一)
    • gdi32.lib (可能需要,具体取决于你的OpenSSL版本和配置)

3. CMake 项目配置

如果你使用CMake,它会自动处理这些。

cmake_minimum_required(VERSION 3.10)
project(OpenSSLRSAExample CXX)
# 查找OpenSSL库
find_package(OpenSSL REQUIRED)
add_executable(generate_rsa_keys main.cpp)
target_link_libraries(generate_rsa_keys PRIVATE OpenSSL::SSL OpenSSL::Crypto)

C++ 源代码 (main.cpp)

#include <iostream>
  #include <string>
    #include <fstream>
      #include <vector>
        // OpenSSL 头文件
        #include <openssl/rsa.h>
          #include <openssl/pem.h>
            #include <openssl/err.h> // 用于错误处理
              #pragma warning(disable: 4996)
              // 辅助函数:显示OpenSSL错误
              void printOpenSSLError(const std::string& msg) {
              std::cerr << msg << std::endl;
              ERR_print_errors_fp(stderr);
              }
              // 生成RSA密钥对并保存到PEM文件
              bool generateRSAKeyPair(const std::string& privateKeyFile, const std::string& publicKeyFile, int bits = 2048) {
              RSA* rsa = NULL;
              BIGNUM* bne = NULL; // RSA 公钥指数
              BIO* bp_public = NULL;
              BIO* bp_private = NULL;
              int ret = 0; // 返回值
              // 写入加密的私钥,需要密码
              const char* password = "mypassword"; // 替换为你的密码,或者在实际应用中动态获取
              // 1. 生成 RSA 密钥对
              // 设置公钥指数,通常是 65537
              bne = BN_new();
              if (bne == NULL) {
              printOpenSSLError("Error creating BIGNUM");
              goto err;
              }
              ret = BN_set_word(bne, RSA_F4); // RSA_F4 is 65537
              if (ret != 1) {
              printOpenSSLError("Error setting BIGNUM word");
              goto err;
              }
              // 生成RSA密钥
              rsa = RSA_new();
              if (rsa == NULL) {
              printOpenSSLError("Error creating RSA object");
              goto err;
              }
              ret = RSA_generate_key_ex(rsa, bits, bne, NULL);
              if (ret != 1) {
              printOpenSSLError("Error generating RSA key");
              goto err;
              }
              std::cout << "RSA key pair generated successfully (bits: " << bits << ")." << std::endl;
              // 2. 保存私钥到 PEM 文件
              bp_private = BIO_new_file(privateKeyFile.c_str(), "w+");
              if (bp_private == NULL) {
              printOpenSSLError("Error creating private key file BIO");
              goto err;
              }
              // PEM_write_bio_RSAPrivateKey(bp_private, rsa, NULL, NULL, 0, NULL, NULL); // 未加密的私钥
              if (!PEM_write_bio_RSAPrivateKey(bp_private, rsa, EVP_des_ede3_cbc(), (unsigned char*)password, strlen(password), NULL, NULL)) {
              printOpenSSLError("Error writing private key to file");
              goto err;
              }
              std::cout << "Private key saved to " << privateKeyFile << std::endl;
              // 3. 保存公钥到 PEM 文件
              bp_public = BIO_new_file(publicKeyFile.c_str(), "w+");
              if (bp_public == NULL) {
              printOpenSSLError("Error creating public key file BIO");
              goto err;
              }
              if (!PEM_write_bio_RSA_PUBKEY(bp_public, rsa)) { // RSA_PUBKEY writes in PKCS#8 format
              printOpenSSLError("Error writing public key to file");
              goto err;
              }
              std::cout << "Public key saved to " << publicKeyFile << std::endl;
              ret = 1; // 成功
              err:
              // 清理资源
              if (bp_private != NULL) BIO_free_all(bp_private);
              if (bp_public != NULL) BIO_free_all(bp_public);
              if (bne != NULL) BN_free(bne);
              if (rsa != NULL) RSA_free(rsa);
              return (ret == 1);
              }
              int main() {
              // 1. 初始化 OpenSSL 库
              // 可以调用 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
              // 或者更简单的,使用 ERR_load_crypto_strings();
              ERR_load_crypto_strings(); // 加载错误字符串,以便 ERR_print_errors_fp 可以打印有意义的信息
              OpenSSL_add_all_algorithms(); // 加载所有加密算法
              std::string privateKeyPath = "private_key.pem";
              std::string publicKeyPath = "public_key.pem";
              if (generateRSAKeyPair(privateKeyPath, publicKeyPath)) {
              std::cout << "\nRSA key pair generation and saving completed successfully." << std::endl;
              } else {
              std::cerr << "\nFailed to generate RSA key pair." << std::endl;
              }
              // 2. 清理 OpenSSL 库
              EVP_cleanup(); // 清理所有算法
              ERR_free_strings(); // 释放错误字符串
              return 0;
              }

编译和运行

使用 CMake (推荐):

  1. 创建 main.cppCMakeLists.txt 在同一个目录。
  2. 在终端中导航到该目录。
  3. 创建 build 目录并进入:
    mkdir build
    cd build
  4. 运行 CMake 生成项目文件 (例如,Visual Studio 项目文件):
    cmake .. -G "Visual Studio 17 2022" # 根据你的VS版本调整
    或在 Linux/macOS 上:
    cmake ..
  5. 构建项目:
    cmake --build . --config Release # 或 Debug
  6. 运行可执行文件 (generate_rsa_keys.exebuild/Releasebuild/Debug 目录下)。

手动编译 (Windows, GCC/MinGW):

g++ main.cpp -o generate_rsa_keys -I C:\OpenSSL-Win64\include -L C:\OpenSSL-Win64\lib -lssl -lcrypto -lws2_32 -lgdi32

(请替换 C:\OpenSSL-Win64 为你的 OpenSSL 安装路径)

手动编译 (Linux/macOS, GCC/Clang):

g++ main.cpp -o generate_rsa_keys -lssl -lcrypto

预期输出

成功运行后,你会在程序所在的目录看到两个新文件:

  • private_key.pem (包含加密的RSA私钥)
  • public_key.pem (包含RSA公钥)

控制台输出会是这样:

RSA key pair generated successfully (bits: 2048).
Private key saved to private_key.pem
Public key saved to public_key.pem
RSA key pair generation and saving completed successfully.

PEM 文件内容示例

private_key.pem (部分内容,由于加密,格式会有点不同):

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,BA0A9F1C02EB1D53
MIIGsAYJKoZIhvcNAQEE... (大量base64编码的数据) ...
-----END RSA PRIVATE KEY-----

public_key.pem (部分内容):

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... (大量base64编码的数据) ...
-----END PUBLIC KEY-----

密钥长度 (bits 参数)

generateRSAKeyPair 函数中,bits 参数控制生成RSA密钥的长度。

  • 1024: 曾经常用,但现在被认为安全性不足。
  • 2048: 当前推荐的标准,提供良好的安全性。
  • 30724096: 提供更高的安全性,但生成和使用速度较慢。

加密的私钥

示例中私钥是加密保存的,密码是 “mypassword”。这意味着当你以后需要加载这个私钥时,需要提供相同的密码。如果你想保存未加密的私钥,可以注释掉 PEM_write_bio_RSAPrivateKey 调用中 EVP_des_ede3_cbc() 和密码相关的参数,只留下 NULL但请注意,未加密的私钥非常不安全,应避免在生产环境中使用。

// 保存未加密的私钥 (不推荐用于生产环境)
// if (!PEM_write_bio_RSAPrivateKey(bp_private, rsa, NULL, NULL, 0, NULL, NULL)) {
//     printOpenSSLError("Error writing unencrypted private key to file");
//     goto err;
// }

存在的坑

由于我安装的OpenSSL版本是3.0以上,本文出现的主体代码有一个特点,就是 禁止了4996警告(#pragma warning(disable: 4996)),虽然仅用了这个警告,但毕竟只是临时的一个解决办法,事实上OpenSSL 3.0以上版本代码接口发生了变化,是需要进行修改调整的。
再者,EVP_des_ede3_cbc() 是一个旧的加密算法,OpenSSL 3.0 推荐使用更强的算法,例如 EVP_aes_256_cbc(),这些我将在下一篇博客里填坑。

posted on 2025-09-30 11:12  ljbguanli  阅读(23)  评论(0)    收藏  举报