Crypto++ RSA加密长字符串报消息长度超过公钥最大值问题求解
问题原因
RSA作为非对称加密算法,受填充规则限制天然无法直接加密超过密钥长度对应阈值的明文:3072位RSA使用OAEP-SHA1填充时,最大可加密明文长度就是342字节,这是算法特性不是库的bug。
常见的三种解决方案中,前两种都有明显缺陷:
- 增大密钥长度会让加解密性能指数级下降,4096位RSA的性能仅为3072位的一半不到,且依然有明文长度上限
- 手动切分明文加密的方案存在安全漏洞,还会大幅提升编码复杂度
最优的第三种方案就是行业通用的混合加密(数字信封)方案,Crypto++原生支持该模式的实现,不需要手动切分明文,可直接处理任意长度的字符串、文件数据。
修正后的可运行代码
#include <string> #include <cryptlib.h> #include <rsa.h> #include <osrng.h> #include <iostream> #include <files.h> #include <aes.h> #include <gcm.h> using namespace std; using namespace CryptoPP; void encryptLongText(const string& plain, const RSA::PublicKey& pubKey, string& cipherOut) { AutoSeededRandomPool rng; // 生成随机AES密钥和IV SecByteBlock aesKey(AES::DEFAULT_KEYLENGTH); SecByteBlock iv(AES::BLOCKSIZE); rng.GenerateBlock(aesKey, aesKey.size()); rng.GenerateBlock(iv, iv.size()); // 用RSA加密AES密钥 string encryptedAesKey; RSAES_OAEP_SHA_Encryptor e(pubKey); StringSource ss1(aesKey, aesKey.size(), true, new PK_EncryptorFilter(rng, e, new StringSink(encryptedAesKey) ) ); // 用AES-GCM加密长明文 string encryptedPlain; GCM<AES>::Encryption aesEnc; aesEnc.SetKeyWithIV(aesKey, aesKey.size(), iv, iv.size()); StringSource ss2(plain, true, new AuthenticatedEncryptionFilter(aesEnc, new StringSink(encryptedPlain) ) ); // 拼接结果:加密后的AES密钥长度 + 加密后的AES密钥 + IV + 密文 cipherOut.append((char*)&encryptedAesKey.size(), sizeof(size_t)); cipherOut.append(encryptedAesKey); cipherOut.append((const char*)iv, iv.size()); cipherOut.append(encryptedPlain); } void decryptLongText(const string& cipherIn, const RSA::PrivateKey& privKey, string& plainOut) { AutoSeededRandomPool rng; size_t offset = 0; // 读取加密后的AES密钥长度 size_t encryptedAesKeyLen = *(size_t*)(cipherIn.data() + offset); offset += sizeof(size_t); // 读取加密后的AES密钥 string encryptedAesKey = cipherIn.substr(offset, encryptedAesKeyLen); offset += encryptedAesKeyLen; // 读取IV SecByteBlock iv(AES::BLOCKSIZE); memcpy(iv, cipherIn.data() + offset, AES::BLOCKSIZE); offset += AES::BLOCKSIZE; // 读取密文 string encryptedPlain = cipherIn.substr(offset); // 解密得到AES密钥 SecByteBlock aesKey(AES::DEFAULT_KEYLENGTH); RSAES_OAEP_SHA_Decryptor d(privKey); StringSource ss1(encryptedAesKey, true, new PK_DecryptorFilter(rng, d, new ArraySink(aesKey, aesKey.size()) ) ); // 用AES解密得到明文 GCM<AES>::Decryption aesDec; aesDec.SetKeyWithIV(aesKey, aesKey.size(), iv, iv.size()); StringSource ss2(encryptedPlain, true, new AuthenticatedDecryptionFilter(aesDec, new StringSink(plainOut) ) ); } void b(string plain){ AutoSeededRandomPool rng; InvertibleRSAFunction params; params.GenerateRandomWithKeySize(rng, 3072); RSA::PrivateKey privateKey(params); RSA::PublicKey publicKey(params); std::string cipher, recovered; encryptLongText(plain, publicKey, cipher); decryptLongText(cipher, privateKey, recovered); std::cout << "恢复的明文长度:" << recovered.length() << std::endl; std::cout << "恢复的明文前10位:" << recovered.substr(0, 10) << std::endl; } int main(){ string plain; for(int i=0;i<1000;++i){ plain.push_back('A'); } b(plain); }
方案说明
- 该实现完全复用了原有RSA密钥生成逻辑,只新增了对称加密处理层,符合Crypto++的官方最佳实践
- 可处理任意长度的明文,没有342字节的上限限制,同时性能远高于切分RSA加密、增大密钥长度的方案
- 额外增加了GCM认证加密能力,可自动校验密文是否被篡改,安全性更高
内容的提问来源于stack exchange,提问作者user1407220
相关产品推荐
相关产品推荐

