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

news/2025/9/30 11:15:00/文章来源:https://www.cnblogs.com/ljbguanli/p/19120250

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

文章目录

    • VC++ 使用OpenSSL创建RSA密钥PEM文件
      • 准备工作
      • C++ 源代码 (`main.cpp`)
      • 编译和运行
      • 预期输出
      • PEM 文件内容示例
      • 密钥长度 (`bits` 参数)
      • 加密的私钥
      • 存在的坑

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

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

准备工作

1. 安装 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 密钥对// 设置公钥指数,通常是 65537bne = BN_new();if (bne == NULL) {printOpenSSLError("Error creating BIGNUM");goto err;}ret = BN_set_word(bne, RSA_F4); // RSA_F4 is 65537if (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 formatprintOpenSSLError("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(),这些我将在下一篇博客里填坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/922755.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF1699D Almost Triple Deletions

被神秘贪心标签误导了。 你考虑答案的最终形式长什么样,就是保留若干个相同的数,再将其中间的区间整段整段删干净。 你先枚举保留什么数,然后发现我们可以设 \(f_{i}\) 表示到了第 \(i\) 个位置最多能保留多少个数,…

QMT回测模式为什么要在副图进行

在QMT系统中,回测必须以副图模式进行,主要有以下原因: (1)数据处理与性能优化 副图模式允许策略专注于历史数据的分析和计算,避免与主图的实时行情显示产生冲突。回测过程中,系统需要遍历大量历史K线数据,副图…

DAY20 Channel(通道)NIO(同步,非阻塞)、Selector(选择器)、NIO2-AIO(异步、非阻塞) - 指南

DAY20 Channel(通道)NIO(同步,非阻塞)、Selector(选择器)、NIO2-AIO(异步、非阻塞) - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impor…

详细介绍:Servlet完全上手:核心语法与生命周期详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:支持17种方言10种外语!阿里最新AI语音合成模型Qwen3-TTS-Flash震撼发布

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

判断权限通过遍历二叉树路由删除权限不展示的前端组件

判断权限通过遍历二叉树路由删除权限不展示的前端组件点击查看代码 def clean_node(nodes, full_name):names = full_name.split(.)current_name = names[0]for i, node in enumerate(nodes):assert isinstance(node, …

外国人做的网站wordpress主题大全

文章目录 rollup watch 实现流程watchWatchEmitter 实现 watchInternalWatcher 管理整个 watch 阶段Task 运行任务FileWatcher 实现文件监听 rollup watch 实现流程 每一个配置了 watch 的配置项都会变成一个 Task 任务&#xff0c;每个任务通过 FileWatcher 即 chokidar 进行…

DSA:DeepSeek Sparse Attention

DeepSeek-V3.2-Exp 是一个基于稀疏注意力机制(DSA,DeepSeek Sparse Attention)优化的长上下文处理模型,其核心创新在于高效地处理长序列输入,同时保持模型性能。🧠 什么是 DSA(DeepSeek Sparse Attention)? …

网站资讯如何做成都网站开发哪家好

PHPExcel是一个PHP类库&#xff0c;用来帮助我们简单、高效实现从Excel读取Excel的数据和导出数据到Excel。下面是PHPExcel读取的使用教程&#xff1a;1.首先下载PHPExcel2.下载好文件&#xff0c;解压可以得到如下文件&#xff1a;为了使用方便&#xff0c;我们可以在根目录创…

荒野猎手出击!启明智显ZX7981PO:专治各种恶劣环境的5G插卡路由器

在工地、在巴士、在露营地、在户外直播现场……你是否曾因网络不稳定而错失重要时刻?是否曾因设备无法承受严苛环境而影响工作进度?现在,启明智显ZX7981PO荒野猎手来了! 这款专为户外严苛场景设计的5G插卡路由器,…

AWS CDK重构功能发布:安全重构基础设施即代码

AWS宣布推出CDK重构功能,帮助开发者在重命名构造、跨堆栈迁移资源和重组CDK应用时保留现有AWS资源,避免因逻辑ID变更导致的资源替换风险,显著提升基础设施代码重构的安全性。我们很高兴宣布一项新的AWS Cloud Devel…

开发即时通社交软件APP首选系统,可定制开发,可提供源码

开发即时通社交软件APP首选系统,可定制开发,可提供源码一、产品定位​信贸通即时通(SEMOT Instant messaging system),是一款跨平台可定制的 P2P 即时通信系统。它专为电子商务网站、各行业门户网站及企事业单位打…

死锁的处理策略-死锁的检测和解除

这两个是允许死锁发生的方法 死锁的检测 为了能对系统是否已经发生了死锁进行检测,必须: 1.用某种数据结构来保存资源的请求和分配信息; 2.提供一种算法。利用上述的信息来检测系统是否已经进入了死锁状态 数据结构…

实用指南:上下文工程驱动智能体向 “连续性认知”跃迁

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

实用指南:基于STM32单片机的OneNet物联网粉尘烟雾检测系统

实用指南:基于STM32单片机的OneNet物联网粉尘烟雾检测系统pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consol…

青岛专业公司网站设计wordpress自动安装

HTML学习第三天&#xff01; PS&#xff1a;牛牛只是每天花了1.5-2小时左右来学习HTML。 书接上回 HTML<div>和<span> HTML 可以通过<div> 和 <span>将元素组合起来。 HTML 区块元素 大多数 HTML 元素被定义为块级元素或内联元素。 块级元素在浏…

springboot3 mybatis 数据库操控入门与实战

springboot3 mybatis 数据库操控入门与实战pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

青岛网站设计工作室清远哪里有网页设计培训学费

比较器是一个简单的概念-在输入端对两个电压进行比较。输出为高或者低。因此&#xff0c;在转换的过程中为什么存在振荡。 当转换电平缓慢改变的时候&#xff0c;这个现象经常会发生。常常是由于输入信号存在噪声&#xff0c;因此在转换电平附近的轻微波动会引起输出端的振荡。…

解决winform调用wpf窗体时原窗体缩小的问题

在使用winform调用wpf窗体时,原来的winform窗体会缩小,同时分辨率会发生变化,用如下方法来解决这个问题。 方法一、 首先找到winform项目中的Properties ==>AssemblyInfo.cs,打开该文件,在末尾加入如下代码,之…

便宜的seo网站优化排名专业酒店设计网站建设

OTA简介 随着设备系统日新月异&#xff0c;用户如何及时获取系统的更新&#xff0c;体验新版本带来的新的体验&#xff0c;以及提升系统的稳定性和安全性成为了每个厂商都面临的严峻问题。OTA&#xff08;Over the Air&#xff09;提供对设备远程升级的能力。升级子系统对用户…