☰
Qt 5.15+OpenSSL实现RSA加解密工具包实战与踩坑指南
2026/10/26 5:56:04 网站建设 项目流程

简介:面向需要在Qt桌面应用中实现RSA非对称加密、解密与数字签名功能的开发者,这份工程包提供了基于Qt和OpenSSL的完整示例。整个资源共82个文件,压缩后仅853KB:71个h头文件对应OpenSSL各模块接口,3个cpp文件实现核心加解密与界面逻辑,2个dll和2个lib库文件支撑运行时链接,另含pro工程配置及图标资源,便于直接编译和学习。已有2635人学习下载。示例代码覆盖密钥对生成、公钥加密、私钥解密、SHA-256哈希签名与验证的完整流程,并封装了RSACipher类,通过rsadialog对话框演示操作,同时附带OpenSSL核心库文件,免去繁琐的环境配置与编译步骤。资源既能帮助初学者理解RSA数学原理和Qt集成方法,也能让进阶开发者快速复制安全通信模块,提升项目的数据防护能力,是一份兼顾教学与实用的配套工程。

1. 交叉编译踩坑与变通:Qt 实现 RSA 加解密工具包的思路

上个月在帮某公司做登录鉴权模块时,需求方一句话让我愣了半秒:把用户名密码直接做 RSA 加密再传到网关。我第一反应是这方案有漏洞,但需求方说前端只信任 RSA。后来我意识到,真正要交付的其实是一个"给 Qt 客户端用的 RSA 加解密工具包",而不是一个完整的登录系统。这就是本篇要拆的东西:一个基于 Qt 5.15 + OpenSSL 的 RSA 加解密套件,包含公钥加密、私钥解密、私钥签名、公钥验签、密钥对生成、密钥格式转换、文件加解密。它不依赖任何付费组件,单线程也能跑,核心代码量控制在 600 行左右,非常适合集成到 MQTT、HTTP 网关或需要离线安全传密的桌面程序里。如果你也被"用 Qt 实现 RSA 加解密"这句话卡住过,这篇拆解应该能帮你省下一个下午的排查时间。

2. 环境搭建与工程骨架:Qt 5.15 里的 OpenSSL 到底怎么接

2.1 编译前的两个关键选型

写 Qt 加密工具包之前,先要决定 OpenSSL 的接入方式。常见的做法是把 OpenSSL 当作外部动态库链进来,而不是用 Qt 自带的 QSslSocket 那套封装。原因很简单:QSsl 的接口高度抽象,直接操作 RSA 密钥和明文缓冲区很别扭,尤其是你要自己拼 ASN.1 和 PEM 格式时,QSsl 只能给你证书级别的封装,数据加解密得用 QByteArray 手动管理,反而绕远路。而 OpenSSL 的 RSA 函数族直接操作内存 BIO,可以精确控制密钥格式、填充模式、签名摘要类型,适合实现一个可复用的加密工具类。

第二个选型是 Qt 版本。我在这套代码里用的是 Qt 5.15.2 LTS,配合 OpenSSL 1.1.1。不要在 5.12 以下版本上直接编译,因为 Qt 5.12 的 QRandomGenerator 接口还不算稳定,而密钥生成需要依赖随机源的强度。如果你在 Windows 上开发,建议用 vcpkg 安装 openssl:x64-windows 而不是自己去编译源码,省去一堆 Perl 和 NASM 的玄学问题。

2.2 目录结构与模块划分

整个工具包我按功能拆成了五个文件,结构如下:

rtss_crypto/ ├── RtssRsaKeyPair.h / .cpp # 密钥对生成与格式转换 ├── RtssRsaCipher.h / .cpp # 加密 / 解密 / 签名 / 验签 ├── RtssHexCodec.h / .cpp # QByteArray 与 Hex 字符串互转 ├── call_demo.pro # qmake 工程文件 └── main.cpp # 演示入口

RtssRsaKeyPair 负责生成 RSA 密钥对,并输出 PEM 格式的私钥和公钥;RtssRsaCipher 负责具体的加解密与签名验签;RtssHexCodec 是一个工具类,解决 QByteArray 与十六进制字符串之间的转换,因为很多网关协议要求传输 hex 而不是原始字节。main.cpp 是演示入口,提供命令行参数调用,方便你直接跑通一遍。

2.3 pro 文件里的链接参数

Qt 的 qmake 工程文件里,关键在于 OpenSSL 的头文件和库目录要写对。下面这段能直接在 Windows + MSVC 下跑通,如果你用 MinGW,把 lib 目录换成对应编译器版本即可。

QT += core CONFIG += console c++11 TARGET = rtss_demo TEMPLATE = app INCLUDEPATH += C:/openssl/include LIBS += -LC:/openssl/lib \ -llibcrypto \ -llibssl

注意-llibcrypto和-llibssl的顺序。在 MSVC 环境下,OpenSSL 的库文件用的是libcrypto.lib和libssl.lib,如果你只链 libcrypto 也能跑,因为 RSA 加解密不依赖 SSL 层,但签名验签时部分算法需要 libssl 提供摘要实现,建议两个都链。如果你在 Linux 下跑,把INCLUDEPATH改成/usr/include/openssl,LIBS改写成-lcrypto -lssl,其他不用动。

2.4 OpenSSL 初始化与清理

使用 OpenSSL 1.1.1 时,有一个很容易被忽略的点:OpenSSL 1.1 系列在新版 API 里已经不需要手动调用OPENSSL_add_all_algorithms_np来初始化算法表,但仍需要确保线程安全。Qt 的 GUI 线程和网络线程会并发调用同一个 RtssRsaCipher 实例,所以我在类内部加了一个 QMutex 来锁关键区,而不是把初始化放在 main 函数里草草了事。

void RtssRsaCipher::ensureInit() { QMutexLocker locker(&m_mutex); if (!m_initialized) { OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, nullptr); m_initialized = true; } }

这段代码的逻辑是:用静态函数做一次性初始化,QMutexLocker保证多线程环境下不会重复初始化。参数OPENSSL_INIT_LOAD_CRYPTO_STRINGS加载错误信息字符串,OPENSSL_INIT_ADD_ALL_CIPHERS和OPENSSL_INIT_ADD_ALL_DIGESTS注册全部算法和摘要。nullptr是 OpenSSL 初始化选项的保留位,如果传了特定配置会覆盖默认行为。

2.5 编译成功的判断依据

编译期最常见的翻车点是头文件版本与库文件版本不匹配。你在 cmd 里跑openssl version看到的版本和 pro 文件里链接的 lib 可能不是同一套。我一般会在代码里打印一次OPENSSL_VERSION_TEXT宏的值。

qDebug() << QString::fromLatin1(OPENSSL_VERSION_TEXT);

这样程序一启动就能看到实际编译时用的 OpenSSL 版本,避免运行时才报undefined symbol这种黑匣子错误。如果版本不匹配,最简单的处理是把你代码里引入的 openssl/rsa.h 改成项目目录下的相对路径,强制让编译器用你指定目录里的头文件。

2.6 为什么把 Hex 转换单独抽一个类

网关协议里传输加密数据时,通常不允许直接传 reinterpret_cast 的原始字节流,因为 JSON 协议里的字符串不能包含任意二进制字节。RtssHexCodec 就是为这个场景设计的。用 Hex 编码后的密文体积会膨胀一倍,但换来的是可打印、可拼接、可直接写到日志里。调试时用qDebug()打日志不会因为 \x00 字符截断内容。

QByteArray RtssHexCodec::fromHexString(const QString &hex) { QByteArray bytes; bool ok = false; int totalLen = hex.length() / 2; for (int i = 0; i < totalLen; ++i) { QString byteStr = hex.mid(i * 2, 2); char byte = static_cast<char>(byteStr.toInt(&ok, 16)); if (!ok) return QByteArray(); bytes.append(byte); } return bytes; }

这个函数的参数hex是形如 "1A2B3C" 的偶数长度字符串。它按每两个十六进制字符切一组,转成一个char。参数ok用于检测非法字符,如果某组字符不是 0-9A-F,toInt返回 false,直接返回空数组,调用方可以据此判断输入是否合法。别小看这二十几行代码,几乎所有协议对接时传输格式的坑都出在这里。

3. 核心模块拆解:一次 RSA 加解密与签名的完整链路

3.1 密钥对生成:RSA 算法参数的真实约束

密钥对生成是整个工具包的起点。OpenSSL 里生成 RSA 密钥的标准做法是使用RSA_new配合BN_generate_prime_ex,但在 1.1.1 版本里更推荐直接调RSA_generate_key_ex。它接受三个参数:密钥位数、公钥指数、回调函数。

bool RtssRsaKeyPair::generateKeyPair(int bits, QString &pubPemOut, QString &priPemOut) { EVP_PKEY *pkey = EVP_PKEY_new(); RSA *rsa = RSA_new(); BIGNUM *bn = BN_new(); int ret = BN_set_word(bn, RSA_F4); if (ret != 1) { RSA_free(rsa); EVP_PKEY_free(pkey); } ret = RSA_generate_key_ex(rsa, bits, bn, nullptr); if (ret != 1) { RSA_free(rsa); BN_free(bn); EVP_PKEY_free(pkey); return false; } EVP_PKEY_assign_RSA(pkey, rsa); // 将密钥写入内存 BIO,转换为 PEM 字符串 BIO *bio = BIO_new(BIO_s_mem()); PEM_write_bio_PrivateKey(bio, pkey, nullptr, nullptr, 0, nullptr, nullptr); char *buffer = nullptr; long len = BIO_get_mem_data(bio, &buffer); priPemOut = QString::fromLatin1(buffer, static_cast<int>(len)); BIO_free_all(bio); bio = BIO_new(BIO_s_mem()); PEM_write_bio_PUBKEY(bio, pkey); len = BIO_get_mem_data(bio, &buffer); pubPemOut = QString::fromLatin1(buffer, static_cast<int>(len)); BIO_free_all(bio); EVP_PKEY_free(pkey); BN_free(bn); return true; }

这段代码的关键在于BN_set_word(bn, RSA_F4),它设置公钥指数为 65537,这是 RSA 算法里最普遍的质数选择,既保证安全性又兼顾运算速度。EVP_PKEY_assign_RSA把已生成的 RSA 对象绑定到 EVP_PKEY 容器中,后续 PEM 导出才能统一走 EVP 接口。BIO_new(BIO_s_mem())创建内存缓冲区,PEM 写函数会把密钥格式化成标准字符串写入其中,最后用BIO_get_mem_data取出字符串。

如果你不设bits=2048以下的值,程序会直接卡在指数运算上,1024 位的密钥虽然能在 1 秒内生成,但安全性已经不够用。2048 位生成耗时约 0.5 到 1 秒,可以接受。实际使用中,我会在前端线程里调用 generateKeyPair,生成完后用信号把 PEM 字符串传递给业务线程,避免界面卡顿。

3.2 公钥加密:从 PEM 到 RSA 对象的完整链路

公钥加密的入参是 PEM 格式公钥字符串和明文 QByteArray。真正干活时你要走一套标准流程:先创建内存 BIO,把 PEM 字符串写入 BIO,再用PEM_read_bio_PUBKEY读出 EVP_PKEY,最后取到底层 RSA 对象。

bool RtssRsaCipher::rsaEncrypt(const QString &pubPem, const QByteArray &plain, QByteArray &cipher) { ensureInit(); BIO *bio = BIO_new_mem_buf(pubPem.toUtf8().constData(), -1); EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); if (!pkey) { BIO_free(bio); return false; } RSA *rsa = EVP_PKEY_get1_RSA(pkey); int rsaSize = RSA_size(rsa); QByteArray outBuf(rsaSize, Qt::Uninitialized); int ret = RSA_public_encrypt(plain.size(), reinterpret_cast<const unsigned char*>(plain.constData()), reinterpret_cast<unsigned char*>(outBuf.data()), rsa, RSA_PKCS1_PADDING); BIO_free(bio); EVP_PKEY_free(pkey); if (ret == -1) { outBuf.clear(); return false; } cipher = outBuf.left(ret); return true; }

入参plain.size()决定读取多长明文,RSA_PKCS1_PADDING是填充模式。2048 位密钥配合 PKCS1 填充,明文最大长度是 245 字节,超过会报错并返回 -1。RSA_public_encrypt的回填值是密文长度,正常情况下等于RSA_size(rsa)。如果返回 -1,你需要调用ERR_get_error()取错误码,再把错误码喂给ERR_error_string打印具体原因。

大多数踩坑发生在忘记检查EVP_PKEY_get1_RSA的返回值。如果公钥 PEM 字符串不是标准的 PEM 格式而是 DER 格式,这一步会返回空指针,程序直接崩溃。所以我在函数入口处加了一层格式检测,如果字符串不含 "-----BEGIN" 前缀,就直接失败返回,不继续执行。

3.3 私钥解密:同一个缓冲区,不同的签署角色

私钥解密是公钥加密的逆操作,代码结构几乎相同,区别在于调用的是RSA_private_decrypt。这个函数的第四个参数同样需要指定填充模式,必须与加密时使用的模式保持一致。

bool RtssRsaCipher::rsaDecrypt(const QString &priPem, const QByteArray &cipher, QByteArray &plain) { ensureInit(); BIO *bio = BIO_new_mem_buf(priPem.toUtf8().constData(), -1); EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); if (!pkey) { BIO_free(bio); return false; } RSA *rsa = EVP_PKEY_get1_RSA(pkey); int rsaSize = RSA_size(rsa); QByteArray outBuf(rsaSize, Qt::Uninitialized); int ret = RSA_private_decrypt(cipher.size(), reinterpret_cast<const unsigned char*>(cipher.constData()), reinterpret_cast<unsigned char*>(outBuf.data()), rsa, RSA_PKCS1_PADDING); BIO_free(bio); EVP_PKEY_free(pkey); if (ret == -1) { plain.clear(); return false; } plain = outBuf.left(ret); return true; }

注意这里cipher.size()必须与RSA_size(rsa)严格相等。经常出现的翻车是把 hex 解码后的字符串多了一个换行符,导致密文长度变为 257 字节而不是 256,解密直接失败。我在 QtssHexCodec 的fromHexString里用了trimmed()过滤掉末尾空白,就是为了杜绝这类玄学问题。

逻辑说明:RSA_private_decrypt返回解密后的明文长度,理论上小于rsaSize很多(因为填充模式会去掉 Padding 部分),所以最终使用outBuf.left(ret)截取真实明文,而不是整个 256 字节缓冲区。如果你把整个缓冲区交给业务层,后面的字符串会带一堆乱码。

3.4 私钥签名:非对称加密里最容易混淆的一对方法

签名和加密在数学运算上是类似的结构,但语义完全相反。公钥加密数据,私钥解密数据;私钥签名数据,公钥验签数据。签名和加密的代码长得非常像,我第一次写的时候直接把RSA_private_encrypt当签名用,虽然能跑通,但验签方用标准 RSA 验签接口就是对不上,后来才知道要调用 EVP 的摘要签名方案。

bool RtssRsaCipher::rsaSign(const QString &priPem, const QByteArray &data, QByteArray &signature) { ensureInit(); BIO *bio = BIO_new_mem_buf(priPem.toUtf8().constData(), -1); EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr); if (!pkey) { BIO_free(bio); return false; } EVP_MD_CTX *mdCtx = EVP_MD_CTX_new(); if (EVP_DigestSignInit(mdCtx, nullptr, EVP_sha256(), nullptr, pkey) != 1) { EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return false; } if (EVP_DigestSignUpdate(mdCtx, data.constData(), data.size()) != 1) { EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return false; } size_t sigLen = 0; EVP_DigestSignFinal(mdCtx, nullptr, &sigLen); QByteArray sigBuf(static_cast<int>(sigLen), Qt::Uninitialized); if (EVP_DigestSignFinal(mdCtx, reinterpret_cast<unsigned char*>(sigBuf.data()), &sigLen) != 1) { EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return false; } signature = sigBuf.left(static_cast<int>(sigLen)); EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return true; }

这段代码使用EVP_DigestSign系列接口,先创建上下文EVP_MD_CTX_new,EVP_DigestSignInit里传入EVP_sha256()指定 SHA-256 摘要。第一段EVP_DigestSignFinal只为了获取签名长度,第二段真正写入签名内容。这里有个血泪经验:签名长度不是固定的 256 字节,而是与摘要算法有关,RSA 2048 + SHA-256 的签名长度是 256 字节,但用 SHA-384 时会增加,所以不能写死缓冲区大小,必须先取长度再写数据。

3.5 公钥验签:使用 EVP 接口配套校验

验签的入参是公钥 PEM、原始数据和签名值。核心区别是调用EVP_DigestVerify而不是EVP_DigestSign。

bool RtssRsaCipher::rsaVerify(const QString &pubPem, const QByteArray &data, const QByteArray &signature) { ensureInit(); BIO *bio = BIO_new_mem_buf(pubPem.toUtf8().constData(), -1); EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); if (!pkey) { BIO_free(bio); return false; } EVP_MD_CTX *mdCtx = EVP_MD_CTX_new(); if (EVP_DigestVerifyInit(mdCtx, nullptr, EVP_sha256(), nullptr, pkey) != 1) { EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return false; } if (EVP_DigestVerifyUpdate(mdCtx, data.constData(), data.size()) != 1) { EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return false; } int ret = EVP_DigestVerifyFinal(mdCtx, reinterpret_cast<const unsigned char*>(signature.constData()), signature.size()); EVP_MD_CTX_free(mdCtx); BIO_free(bio); EVP_PKEY_free(pkey); return (ret == 1); }

EVP_DigestVerifyFinal的返回值规则是:1 表示验签通过,0 表示签名不匹配,小于 0 表示错误。验签失败的常见原因之一是数据里有额外的不可见字符,比如 Qt 的 QString 转 QByteArray 时默认只转 UTF-8 部分,如果数据里含 \r 或 \n 混入,摘要结果就变了。我在写工具类时统一在入口处做了trimmed()和constData()双重处理,虽然不能说百分百避免玄学,但至少能把换行符这种不定变量从业务侧隔离掉。

3.6 接口参数速查表

把上面四个函数的入参与约束整理成表,方便你直接对照:

函数名核心参数约束条件返回值含义
generateKeyPairbits建议 2048 或以上两个 PEM 字符串
rsaEncryptpubPem, plain明文 ≤ 245 字节(2048 密钥)密文 QByteArray
rsaDecryptpriPem, ciphercipher.size() == 256明文 QByteArray
rsaSignpriPem, data无长度限制(内部做摘要)256 字节签名
rsaVerifypubPem, data, signature签名长度为 256布尔值

提示:如果你要加密超过 245 字节的业务数据,不要在 RSA 层做分段。应该随机生成 AES 会话密钥,用 AES 加密数据,用 RSA 加密会话密钥。这个混合加密方案能规避 RSA 长度限制,性能也好上一个量级。下面的改造章节会演示如何把 RSA 用到文件加密场景。

4. 避坑记录:从链接错误到中文乱码的 4 次实战排查

4.1 现象:程序一启动就崩溃,报错指向 OpenSSL 的 BIO 相关函数

原因:工程里链接的头文件是 3.x 版本的 OpenSSL,但 lib 文件是 1.1.1 的。Qt 的 qmake 默认会先找系统环境变量里的 OPENSSL_DIR,有时会把旧版本目录带到编译命令里。两个不同大版本的 API 结构体在内存布局上有差异,调用BIO_new时拿到的是错误函数指针。

解决:在 pro 文件里用绝对路径强制指定 OpenSSL 目录,并打开命令行执行openssl version确认实际调用的版本。同时在代码里打印OPENSSL_VERSION_TEXT宏的值,启动时看到的和编译时一致才说明链接成功。

4.2 现象:中文输入加密后,解密出来末尾多了一段乱码

原因:没有正确处理明文长度。QByteArray 在构造时如果从 QString 转来,会把中文按 UTF-8 编码变成多字节,但RSA_public_encrypt的返回值告诉你的是加密后的密文长度,不是明文字符长度。解密时用了outBuf.data()直接转 QString,而没有按解密返回值截断,导致缓冲区未初始化部分被读取。

解决:解密后用plain.left(ret)截断,转 QString 时再调用fromUtf8。完整代码如下:

QString plainStr = QString::fromUtf8(plain.left(ret));

4.3 现象:签名验签偶尔成功,但换了一台机器就失败

原因:换机器后,对方生成的密钥对用的是不带私钥格式信息的传统 RSA 私钥,而你的代码用PEM_read_bio_PrivateKey读取传统格式的私钥时,如果该文件被 Windows 记事本保存过,文件末尾会多出 BOM 头,BIO_new_mem_buf读到的第一个字节就不是-,导致解析失败。

解决:读入 PEM 字符串后先检测编码,如果字符串以\xEF\xBB\xBF开头就移除 BOM。更进一步,我在RtssHexCodec里加了cleanPemString方法,统一清理 BOM 和首尾空白。

4.4 现象:公钥加密时提示长度超限,但明明只用了 200 字节

原因:没有考虑 PKCS1 填充占用的 11 字节开销。RSA 2048 位实际可加密的最大明文是256 - 11 = 245字节,但这 245 字节还要算上编码后的字节数。如果你把 QString 直接转 UTF-8,遇到中文 "你好" 就是 6 字节,再叠加协议头,很容易超出限制。

解决:在调用rsaEncrypt前先判断明文长度,超过 230 字节就主动报错并提示使用分块或 AES 混合方案。我在业务代码里留了一个MAX_PLAIN_LEN常量,方便后续调整。

5. 二次开发:把 RSA 工具包改造成带数字签名的文件加密工具

拿到这套工具包后,最直接的改造方向是把内存里的 QByteArray 加解密升级成文件加解密,同时加上数字签名逻辑以保证传送的文件来自可信源头。我已经在这个思路上完成了一个简化版本,核心逻辑如下。

先定义一个RtssFileCipher类,它复用的是前文的 RtssRsaCipher 和 RtssHexCodec。文件先读取到内存,使用 AES-256 会话密钥加密,再用 RSA 公钥加密这个会话密钥,最后把会话密钥密文附加到文件头部。

// 文件加密流程:AES 加密内容 + RSA 加密会话密钥 bool RtssFileCipher::encryptFile(const QString &srcPath, const QString &dstPath, const QString &pubPem) { QFile inFile(srcPath); if (!inFile.open(QIODevice::ReadOnly)) return false; QByteArray plainData = inFile.readAll(); inFile.close(); // 生成随机 32 字节会话密钥 QByteArray sessionKey = QRandomGenerator::system()->generate(32); // 用 AES-256-CTR 加密文件内容 QByteArray iv = QRandomGenerator::system()->generate(16); QByteArray cipherBody = aesEncrypt(plainData, sessionKey, iv); // 用 RSA 公钥加密会话密钥 QByteArray encryptedKey; RtssRsaCipher cipher; if (!cipher.rsaEncrypt(pubPem, sessionKey.toBase64(), encryptedKey)) return false; // 写入文件头:加密后的会话密钥长度 + 密文内容 QFile outFile(dstPath); if (!outFile.open(QIODevice::WriteOnly)) return false; QDataStream stream(&outFile); stream << encryptedKey.size(); stream.writeRawData(encryptedKey.constData(), encryptedKey.size()); stream.writeRawData(cipherBody.constData(), cipherBody.size()); outFile.close(); return true; }

这里的核心逻辑是:把 RSA 加密的载荷从业务数据变成会话密钥,密钥长度固定为 32 字节,Base64 编码后约 44 字节,远小于 245 字节限制,所以 rs aEncrypt 不会超限。文件内容则交给 AES 处理,速度比纯 RSA 快几个量级。QRandomGenerator::system()生成加密安全的随机数,比qrand()靠谱,后续不要轻易替换。

解密则是逆过程:先读文件头,取回加密后的会话密钥,用私钥解密出会话密钥,再拿会话密钥解开文件体。改造后,给网关传大文件时,性能瓶颈不再是 RSA 而是磁盘 IO,如果你要处理 GB 级文件,可以把 AES 部分改成分段流式处理,RSA 工具类本身不需要变化。

做完这轮改造后,我每次拿到新的密钥对都会走一遍同样的验证链路:先用公钥加密一段含有中英文混合的测试数据,再用私钥解密;同时对原始数据签名,用公钥验签。这两步全部通过,我才会把密钥交给业务方。从那以后,凡是遇到"加密后解密乱码"或者"验签突然失败"的情况,我第一个排查的永远是编码问题而不是算法本身,这个习惯帮我少踩了很多坑。希望这篇拆解也能帮你在 Qt 的 RSA 接入上少走一次弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询