本节摘要:RSA 把安全性押在一句人尽皆知、却无人能做到的话上:把两个大素数相乘很容易,把乘积分解回这两个素数极难。本节走完密钥生成、加密、解密的全流程,解释欧拉定理如何保证加解密互逆、2048 与 3072 位模长如何随算力演进、教科书 RSA 的三宗罪,以及它作为签名算法的第二次生命。
1977 年,麻省理工学院的 Ron Rivest、Adi Shamir 与 Leonard Adleman 读到 Diffie-Hellman 的论文后,着手补上"陷门"这个缺口。他们的答案藏在两百年前的数论里:找两个大素数 p 与 q,乘积 n = pq 公开,则凡知道 p 与 q 的人都能轻松算出 n 的欧拉函数 φ(n) = (p-1)(q-1),而不知道的人想算 φ(n) 等价于分解 n。私钥的全部秘密就是这两个素数——大整数分解的难度,就是 RSA 的安全边际。
完整流程只需四步,先用教学级小参数走一遍:
# 教学版 RSA:数字仅两位量级,真实密钥为 2048/3072 位 from math import gcd def make_keys(p, q, e=65537): n, phi = p * q, (p - 1) * (q - 1) d = pow(e, -1, phi) # 求 e 模 phi 的逆元 return (n, e), (n, d) def rsa(msg_int, key): n, k = key return pow(msg_int, k, n) # 加密用 e,解密用 d pub, pri = make_keys(61, 53) # n = 3233 print(pub) # -> (3233, 65537),e 需与 phi 互素(此处恰好成立) m = 65 # 字母 A c = rsa(m, pub) print(c, rsa(c, pri)) # 加密再解密,还原 65
小参数下 d 用扩展欧几里得或内置逆元一步求出;真实实现里 p、q 由密码学安全随机数在大区间内筛出,整个密钥生成在毫秒级完成——生成容易、破解无望,天平的倾斜全在分解难度上。
RSA 的密钥长度指的是模数 n 的二进制位数,而"安全"的参照物始终是分解算法的进步。1990 年代 512 位模长被逐步逼近,1999 年 RSA-155(512 位)被分解成功,互联网界仓促迁移;2010 年 RSA-768 沦陷(约两千 CPU 年的计算);行业共识随之落定为 2048 位起步。NIST 的现行指引给出时间表:2048 位可支撑到 2030 年前后,长期机密应迁移到 3072 位。参照系是 NIST 的安全强度分级:2048 位 RSA 与 128 位对称安全强度相当——记住这个"约 16 倍换 1"的兑换率,4.4 节对比 ECC 时它就是标尺。
分解难度的另一个变量是算法本身:普通数域筛(GNFS)让分解难度以亚指数速度下降,这也是 RSA 模长必须持续加宽的原因。而 1994 年 Shor 提出的量子算法将在多项式时间内完成分解——这把悬在 RSA 头上的剑,第 7 章再郑重出鞘。

用上面的四步公式直接上线,会死得很利索。工程规范(PKCS#1)为此定义了两套填充方案,三宗罪也各有对应解法:
此外还有一条工程常识:RSA 慢且单次只能处理小于模数的数据块(2048 位模数约 245 字节),所以它从不直接加密文件或报文——数据交给 AES,RSA 只负责加密那把 AES 会话密钥(混合加密),或对消息的 SHA-256 摘要签名。签名走"哈希再签"两步,也让任意长度消息的签名成本恒定。
⚠️ 常见坑:把教科书公式当成生产代码。真实场景必须使用经过审计的密码库,并检查填充方案与模长——RSA 的事故几乎全是"自己实现、忘了填充"造成的,而不是数学被攻破。
问:公钥指数为什么固定 65537?它是 2 的 16 次方加 1,二进制里只有两个 1,模幂最快;又足够大以避开低指数攻击的历史坑。在正确填充之后,指数的选择不再影响安全性,65537 是性能与稳健的折中。
问:旧版填充为什么被 OAEP 取代?1998 年布莱肯巴赫演示了逐字节恢复明文的选择密文攻击——解密服务对填充错误的响应差异成了预言机。OAEP 从构造上切断了这条信道,新系统一律应使用 OAEP。
问:RSA 的签名与验证速度为什么不对称?公钥指数小,验签远快于签名;私钥运算借中国剩余定理还能再加速约四倍。这些工程细节决定了 RSA 在证书签名场景依然常见,而在握手加密里早已退场。
RSA 解决了"送信",Diffie-Hellman 则在另一条战线上解决"见面"。下一节看密钥交换的原始魔术:双方从未谋面,如何在众目睽睽下算出同一个秘密。