3. RSA 算法原理:密钥生成、加解密、签名验签与数学基础

RSA 算法,说白了一句话:用两个大质数算出一个公钥和一个私钥,公钥加密的东西只有私钥能解开,私钥签名的东西只有公钥能验证。听起来简单,但落地时坑不少。我最早接触 RSA 是在一个金融支付项目里,当时被要求做双向签名验签,结果因为密钥长度选错,性能直接崩了……嗯,从那以后我对 RSA 的每个参数都格外敏感。

3.1 RSA 密钥生成:核心步骤

密钥生成是 RSA 的起点。我个人习惯把流程拆成五步,每一步都对应一个数学操作,但咱们先不堆公式,先看流程。

  1. 选两个大质数 p 和 q:长度至少 1024 位,实际生产环境我建议 2048 位起步。曾经有个客户为了省性能用了 512 位,结果被暴力破解了……
  2. 计算 n = p × q:n 就是公钥和私钥都依赖的模数,长度决定了 RSA 的安全强度。
  3. 计算欧拉函数 φ(n) = (p-1)(q-1):这个值要严格保密,泄露了就等于私钥丢了。
  4. 选公钥指数 e:常用 65537(0x10001),为什么?因为它是质数,且二进制只有两个 1,加密效率高。
  5. 计算私钥指数 d:d 是 e 关于 φ(n) 的模逆元,即 e × d ≡ 1 (mod φ(n))。

密钥长度对照表(我整理的生产环境推荐值)

密钥长度 安全等级 适用场景 性能表现
1024 位 已不安全 仅用于兼容旧系统 快
2048 位 当前标准 企业级签名验签、TLS 证书 中等
4096 位 高安全 金融、政务、CA 根证书 慢(约 2048 位的 4-8 倍)

⚠ 避坑指南:我曾经见过有人直接用 OpenSSL 默认参数生成密钥,结果 p 和 q 太接近,导致 n 容易被因式分解。生成时一定要加 -check 参数,或者用硬件安全模块(HSM)来保证随机性。

3.2 RSA 加密与解密:数学原理简述

加密解密的数学原理其实就两个公式:

  • 加密:c = me mod n(m 是明文,c 是密文)
  • 解密:m = cd mod n

为什么会这样?因为欧拉定理保证了 me×d ≡ m (mod n)。说白了,就是指数运算的“可逆性”。你想想看,公钥 (n, e) 加密后,只有知道私钥 d 的人才能还原,其他人就算拿到 n 和 e,也解不开——因为不知道 φ(n) 就求不出 d。

但这里有个实际限制:RSA 不能直接加密大块数据。明文 m 必须小于 n,否则模运算会丢失信息。我在项目中通常的做法是:用 RSA 加密对称密钥(比如 AES 的 256 位密钥),再用对称密钥加密实际数据。这叫“混合加密”,既安全又高效。

💡 经验之谈:RSA 加密时,明文长度不能超过密钥长度减去填充开销。比如 2048 位密钥,PKCS#1 v1.5 填充后最多加密 245 字节,OAEP 填充后更少。别问我怎么知道的——线上出过 bug 才知道的。

3.3 RSA 签名与验签:流程与区别

签名验签和加密解密正好相反:私钥签名,公钥验签。我经常跟团队说:加密是“公钥锁,私钥开”;签名是“私钥签,公钥验”。

签名流程:

  1. 对原始数据做哈希(比如 SHA-256),得到摘要 h。
  2. 用私钥 d 对 h 做指数运算:s = hd mod n。
  3. 输出签名 s 和原始数据一起发送。

验签流程:

  1. 接收方用公钥 e 对签名做指数运算:h' = se mod n。
  2. 对原始数据做同样的哈希,得到 h。
  3. 比较 h' 和 h,相等则验签通过。

加密 vs 签名对比

操作 使用密钥 目的 典型场景
加密 公钥加密,私钥解密 保证机密性 传输对称密钥
签名 私钥签名,公钥验签 保证完整性和不可否认性 数字证书、代码签名

⚠ 常见错误:我曾经接手过一个项目,他们把签名和加密搞混了——用公钥去签名,结果验签永远失败。记住:签名只能用私钥,加密只能用公钥,方向不能反。

3.4 RSA 数学原理简述:欧拉定理与模逆元

虽然咱们是实战课,但数学原理得懂个大概,不然出了问题你都不知道怎么排查。我尽量用白话讲。

  • 欧拉函数 φ(n):小于 n 且与 n 互质的正整数个数。对于 n = p×q,φ(n) = (p-1)(q-1)。
  • 欧拉定理:如果 a 和 n 互质,则 aφ(n) ≡ 1 (mod n)。这是 RSA 能工作的根基。
  • 模逆元:e × d ≡ 1 (mod φ(n)),d 就是 e 的模逆元。求 d 用的是扩展欧几里得算法,OpenSSL 底层就是这么算的。

你想想看,为什么 RSA 安全?因为从 n 分解出 p 和 q 是数学难题(大整数因式分解)。只要 p 和 q 足够大、足够随机,目前没有任何算法能在合理时间内破解。但量子计算机来了之后……嗯,那是另一回事了,咱们这门课先不展开。

3.5 RSA 完整流程图

下面这张图是我自己画的 RSA 密钥生成、加密解密、签名验签的全流程。你看一眼就能把整个体系串起来。

RSA 算法全流程框架图 RSA 算法全流程框架图 密钥生成 选 p, q 大质数 n = p × q φ(n) = (p-1)(q-1) e, d 互逆 公钥 (n, e) 公开分发 私钥 (n, d) 严格保密 加密解密 加密: c = me mod n 解密: m = cd mod n 签名验签 签名: s = hd mod n 验签: h' = se mod n 核心要点 • 公钥加密,私钥解密 → 保证机密性 • 私钥签名,公钥验签 → 保证完整性和不可否认性 • 密钥长度推荐 2048 位以上,哈希算法推荐 SHA-256

3.6 填充方案:为什么不能直接加密?

你可能会问:为什么 RSA 不能直接对原始数据做指数运算?因为没有填充的 RSA 是确定性的——同样的明文每次加密结果都一样,攻击者可以穷举。而且,小指数攻击、共模攻击等漏洞都会找上门。

我在项目中只用两种填充方案:

  • PKCS#1 v1.5:兼容性好,但理论上存在 Bleichenbacher 攻击风险。老系统还在用,新系统我建议别碰。
  • OAEP(Optimal Asymmetric Encryption Padding):带随机数的填充,安全性有严格证明。我个人习惯,所有新项目一律用 OAEP。

💡 选型建议:签名用 PSS(Probabilistic Signature Scheme)填充,加密用 OAEP 填充。别混用,混用过一次,调试了一整天才发现是填充模式不匹配。

3.7 性能与安全权衡

RSA 的性能瓶颈在指数运算上。2048 位密钥的私钥操作(解密或签名)比公钥操作(加密或验签)慢一个数量级。为什么?因为私钥指数 d 通常很大,而公钥指数 e 固定为 65537,二进制中 1 的个数少,计算快。

我常用的优化手段:

  • 中国剩余定理(CRT):将私钥运算拆成 mod p 和 mod q 两部分,速度提升约 4 倍。几乎所有生产环境的 RSA 实现都用了 CRT。
  • 缓存公钥指数:如果 e 固定为 65537,可以预计算一些中间值。
  • 硬件加速:HSM 或 CPU 的 AES-NI 指令集对 RSA 也有加速效果。

⚠ 安全提醒:CRT 虽然快,但容易受到故障攻击(Fault Attack)。我曾经在测试中故意注入单比特错误,结果私钥被还原了……生产环境一定要用带校验的 CRT 实现,比如 OpenSSL 的 RSA_blinding_on()。