RSA密钥生成必须使用足够大的素数(如512位),小素数如17、19虽能运行但解密失败率高;教学可用p=61、q=53等小参数,但严禁用于生产;模幂运算需防溢出,填充和编码应调用OpenSSL或Crypto++等成熟库。

RSA密钥生成必须用大素数,别手写小数字
直接用 17 和 19 当 p、q 看似能跑通,但实际加密后解密失败率极高——因为模幂运算在非足够大素数下极易因中间值溢出或同余冲突出错。真实场景中,p 和 q 至少要 512 位(约 154 位十进制),且需经 Miller-Rabin 检验。自己实现素数生成极容易翻车,建议用现成库的随机素数接口,比如 OpenSSL 的 BN_generate_prime_ex 或 Crypto++ 的 Integer::GenerateRandomPrime。
如果你只是验证流程逻辑(非生产),可用预生成的小安全参数组合,例如:p = 61, q = 53 → n = 3233, φ(n) = 3120,再选 e = 17(满足 gcd(e, φ(n)) == 1),算出 d = 2753(用扩展欧几里得求模逆)。这个组合能跑通加解密,但仅限教学演示,绝不能用于真实数据。
加密时明文必须小于 n,否则会丢数据
RSA 要求输入整数 m 满足 0 ≤ m ,否则 <code>m^e mod n 无法唯一还原。常见错误是直接拿字符串字节流当整数传入,比如 "Hello" 转成 0x48656C6C6F(≈ 5×10¹²),远超 n=3233,结果解密出来完全是另一串乱码。
正确做法分两步:
立即学习“C++免费学习笔记(深入)”;
- 对原始数据做填充(如 PKCS#1 v1.5 或 OAEP),把明文“掰碎”成小于
n的块 - 每块转为大整数(用
BigNumber类,如 OpenSSL 的BIGNUM或 Boost.Multiprecision 的cpp_int)再加密 - 若手动模拟,先将字符串按字节转整数,再用
mod n截断 —— 但这会破坏语义,仅适合单字节或纯数值测试
解密失败大概率是模幂实现没处理负数或溢出
C++ 标准整数类型(int、long long)根本存不下 RSA 中间结果。比如 2753^17 mod 3233,光是 2753^17 就有 60+ 位,直接用 pow() 必然溢出或返回 0。
必须用快速模幂(Montgomery reduction 更优,但基础版够用):
int mod_exp(int base, int exp, int mod) {
int res = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) res = (1LL * res * base) % mod; // 关键:1LL 强制 long long 防乘溢出
base = (1LL * base * base) % mod;
exp >>= 1;
}
return res;
}注意三点:
- 每次乘法后立刻
% mod,不能等最后算完再取模 - 用
1LL *把乘积升到long long,避免int * int溢出 - 输入
base先% mod,防止初始值就超范围
不要自己实现填充和编码,直接用 OpenSSL 或 Crypto++
从头写 PKCS#1 v1.5 填充逻辑(包括随机填充字节、固定头部、校验字节)极易出错,且不同语言/库对 ASN.1 编码细节处理不一致,会导致跨平台加解密失败。
真实项目中,直接调用成熟库:
- OpenSSL:
RSA_public_encrypt()/RSA_private_decrypt(),自动处理填充、字节序、内存对齐 - Crypto++:
RSAES_PKCS1v15_Encryptor+PK_EncryptorFilter,API 更 C++ 化 - 务必检查返回值:
RSA_public_encrypt()成功时返回密文长度,失败返回 -1,此时用ERR_get_error()查具体错误
真正难的不是数学公式,而是边界条件:密钥格式(PEM/DER)、内存所有权(谁 free)、线程安全(OpenSSL 1.1+ 需初始化)、以及——你永远不知道对方用的是哪种填充模式。


















