🏠 总目录📚 本教程 14 · RSA ← →
📑 本页目录(点开跳转)

14 · RSA

⏱ 30 分钟 | ⭐ 第一个公钥密码,也是坑最多的一个


🎯 一句话

RSA 用一个漂亮的不对称:把两个大素数乘起来很容易,把乘积拆回去极难。 而教科书上写的那个 RSA,直接用是完全不安全的 —— 这一章会讲清楚差在哪。

两个大素数p、q乘积 n公开相乘:毫秒级 ✅分解:2048 位时,已知算法要天文数字的时间 ❌同一个运算,两个方向的难度天差地别明文 M密文 C明文 MM^e mod n公钥 eC^d mod n私钥 d(要 p、q 才能算出)⭐ 「陷门」就是 p 和 q:知道它们,d 一步就求出来;不知道,就得先分解 n⚠️ 教科书版 RSA 是确定性的 → 同样的明文永远得到同样的密文 → 不满足 IND-CPA所以实际必须加随机填充(OAEP),签名则要用全域哈希
相乘毫秒级,分解却是天文数字 —— 同一个运算,两个方向难度天差地别。⭐ 「陷门」就是 p 和 q:知道它们私钥一步就求出来,不知道就得先分解 n。⚠️ 教科书版 RSA 是确定性的,不满足 IND-CPA —— 实际必须加 OAEP 随机填充。

🔑 一、算法

操作步骤

  1. 【密钥生成】
  2. 选两个大素数 p、q(各 1536 bit)
  3. n = p·q ← 公开
  4. φ(n) = (p−1)(q−1) ← 保密!
  5. 选 e,满足 gcd(e, φ(n)) = 1 ← 通常 e = 65537
  6. d = e⁻¹ mod φ(n) ← 私钥(扩展欧几里得,第 13 章)
  7. 公钥 = (n, e) 私钥 = d(外加 p、q 用于 CRT 加速)
  8. 【加解密】
  9. C = M^e mod n
  10. M = C^d mod n
📐 为什么解密能还原(想看再点)

由 $d = e^{-1} \bmod \varphi(n)$,存在整数 k 使 $ed = 1 + k\varphi(n)$。

情况 1:$\gcd(M, n) = 1$。由欧拉定理 $M^{\varphi(n)} \equiv 1 \pmod n$:

$$C^d = M^{ed} = M^{1+k\varphi(n)} = M\cdot(M^{\varphi(n)})^k \equiv M\cdot 1^k = M \pmod n$$

情况 2:$\gcd(M,n) \ne 1$(M 被 p 或 q 整除)—— 用中国剩余定理分别在 mod p 和 mod q 下验证,结论同样成立。∎

💡 核心就一句:e 和 d 在指数上互为逆元,一乘一除正好回到原点。

💡 为什么 e = 65537

因果链

e = 65537 = 2¹⁶ + 1 = 二进制 10000000000000001
⭐ 只有两个 1→平方-乘算法只需 17 次操作→加密极快
⭐ 又足够大→避免小指数攻击(见下)
⚠️ 历史上用过 e = 3,导致了真实的攻击

💀 二、教科书 RSA 为什么不能直接用

问题 ① 它是确定性的

操作步骤

相同明文→相同密文
⭐ 直接违反 IND-CPA(第 8 章的第一个推论)
💥 实际攻击:如果明文空间小(比如"是"/"否"、一个 4 位数验证码),
攻击者把所有可能的明文都加密一遍,查表即可 💀

问题 ② 乘法同态性可被滥用

对照

⭐ RSA 有一个"意外"的性质:

Enc(M₁) · Enc(M₂) = M₁^e · M₂^e = (M₁·M₂)^e = Enc(M₁ · M₂) mod n

💥 攻击:攻击者想让你解密 C,但你拒绝

他改为让你解密 C' = C · 2^e mod n(看起来是无关的密文)

你返回 M' = 2M

他除以 2 就得到 M ⭐

💥 这个攻击有个名字叫"盲化攻击",它也说明了 为什么绝对不能用同一个密钥对既加密又签名。

问题 ③ 小指数攻击

结果对照

若 e = 3 且消息很短(M³ < n):
C = M³ 根本没有发生模运算
攻击者直接开【三次方根】就还原了 💀
⭐ Håstad 广播攻击:同一条消息用 e=3 发给 3 个不同的人
用中国剩余定理合并 3 个密文→直接解出 M

问题 ④ 共模 / 共因子

结果对照

💥 共模攻击:两个人用相同的 n、不同的 e
任一方都能算出对方的私钥
💥 共因子:如果两个 RSA 公钥的 n 恰好共享一个素数因子
gcd(n₁, n₂) 直接给出那个素数→两个密钥全废
⭐ 这不是理论:2012 年有研究扫描全网 TLS 证书,
发现【数万个】RSA 公钥可以被这样直接分解 ——
根因是嵌入式设备开机时熵不足(第 4 章)

🔧 三、正确用法:必须填充

加密用 OAEP

结果对照

OAEP = Optimal Asymmetric Encryption Padding
把消息 + 【随机数】+ 结构化填充,经过两轮哈希混合,再做 RSA
✅ 引入随机性→满足 IND-CPA
✅ 结构化→破坏乘法同态
✅ 在随机预言机模型下可证 IND-CCA

💥 它取代的 PKCS#1 v1.5 有著名的 Bleichenbacher 攻击(1998): 服务器对"填充格式错误"返回不同响应 → 又是一个预言机 → 约百万次查询就能解密任意密文。 2017 年的 ROBOT 攻击发现,20 年后仍有大量主流网站没修好。

签名用 PSS

结果对照

❌ 直接 σ = M^d mod n(教科书签名)
攻击者可以:σ₁·σ₂ 是 M₁·M₂ 的合法签名 💀(同态性)
也可以先选 σ,反算 M = σ^e,得到一个"合法"的签名 💀
✅ 先哈希再填充:σ = PSS(H(M))^d mod n

🔗 第 17 章会详细讲签名,包括 FDH 和 PSS 的区别。

# ✅ 正确用法
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

key = rsa.generate_private_key(public_exponent=65537, key_size=3072)

ct = key.public_key().encrypt(
    b"secret",
    padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),      # ⭐ 必须 OAEP
                 algorithm=hashes.SHA256(), label=None))

sig = key.sign(b"message",
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()),        # ⭐ 必须 PSS
                salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())

⏱️ 四、侧信道:私钥能被"听"出来

攻击 原理
计时攻击 模幂的时间取决于 d 的比特 → 测时间恢复 d ⭐
功耗分析 平方和乘法的功耗曲线不同,肉眼可辨
Bellcore 故障攻击 CRT 解密中制造一次错误 → 一次错误签名就能分解 n ⭐
缓存攻击 窗口法的查表访问模式泄露指数位

关键信息

🔑 这是第 1 章那条建议的具体理由: 不要自己实现密码算法 —— 数学写对了,实现照样能漏。


📉 五、RSA 今天的地位

关键信息


🔗 和站内其他章的关系

相关的地方 和这一章的关系
第 13 章 φ(pq)=(p−1)(q−1) RSA 的全部秘密 ⭐
第 13 章 欧拉定理 正确性证明
第 13 章 模逆元 d 的计算
第 8 章 确定性⟹不安全 教科书 RSA 的第一宗罪 ⭐
第 4 章 熵不足 共因子攻击的根因
第 7 章 padding oracle Bleichenbacher 是它的公钥版
AI 基础设施 · GPU 到底是什么 计时侧信道的来源就是那边讲 CPU 时列的那几样:分支预测、乱序执行、缓存——正因为执行时间随数据变化,私钥才能被"听"出来 ⭐
Claude 资料库 · 触达生产系统 你给生产 Agent 配的 OAuth / token 签名,底下跑的就是本章的 RSA(或 ECDSA);⚠️ 那一节的结论也一样:认证用标准件,别自己拼填充

✅ 检查点

  1. RSA 的密钥生成五步是什么?哪些必须保密?
  2. 为什么解密能还原明文?核心是哪一句?
  3. 为什么 e 通常取 65537?
  4. 教科书 RSA 的四个问题分别是什么?
  5. 乘法同态性怎么被用来攻击?这说明了什么设计原则?
  6. Håstad 广播攻击是怎么回事?
  7. 共因子攻击的根因是什么?它和第 4 章什么内容有关?
  8. OAEP 提供了哪三样东西?它取代的 PKCS#1 v1.5 有什么问题?
  9. Bellcore 故障攻击有多严重?怎么防?
  10. 新项目该用什么代替 RSA?
👀 答案
  1. ①选大素数 p、q ②n=pq ③φ(n)=(p−1)(q−1) ④选 e 与 φ(n) 互质 ⑤d=e⁻¹ mod φ(n)。p、q、φ(n)、d 都必须保密(知道 φ 就能算出 d)。
  2. 因为 ed = 1 + kφ(n),由欧拉定理 M^φ(n) ≡ 1,所以 C^d = M^(1+kφ(n)) = M·(M^φ(n))^k ≡ M。核心:e 和 d 在指数上互为逆元,一乘一除回到原点。
  3. 65537 = 2¹⁶+1,二进制只有两个 1 → 平方-乘只需 17 次操作,加密极快;同时足够大,避免小指数攻击。
  4. ①确定性(违反 IND-CPA,明文空间小时可枚举)②乘法同态性可被滥用 ③小指数攻击 ④共模/共因子。
  5. Enc(M₁)·Enc(M₂) = Enc(M₁·M₂)。攻击者让你解密 C·2^e,拿到 2M 后除以 2 得到 M(盲化攻击)。说明:绝对不能用同一个密钥既加密又签名。
  6. 同一条消息用 e=3 发给 3 个不同的人,攻击者收集 3 个密文,用中国剩余定理合并后直接开三次方根解出 M。
  7. 嵌入式设备开机时熵不足,导致不同设备生成的 RSA 素数重复。gcd(n₁,n₂) 直接给出共享素数 → 两个密钥全废。和第 4 章的随机数失效是同一根因。2012 年扫描全网发现数万个这样的公钥。
  8. ①随机性(满足 IND-CPA)②结构化填充(破坏乘法同态)③ROM 下可证 IND-CCA。PKCS#1 v1.5 有 Bleichenbacher 攻击:填充错误的不同响应构成预言机,约百万次查询解密任意密文;2017 年 ROBOT 攻击发现 20 年后仍有大量主流网站没修好。
  9. 在 CRT 解密中制造一次错误,仅凭一次错误签名就能分解 n。防御:CRT 后验证签名结果再输出。
  10. 签名用 Ed25519,密钥交换用 X25519,需要抗量子用 ML-KEM / ML-DSA。

🛑 可以停在这里

⚡ 走神救援

先记住这几件事

下一节 👉 15-DH与ElGamal.md ⭐

打卡记录保存在你的浏览器里,首页能看到总进度