RSA¶
CRYPTO · 知识域。RSA 攻击全谱系,密码学方向第一大题库。标签:e和phi不互素、暴力分解N、p&q不当分解N、中国剩余定理、提取PEM文件信息、私钥文件修复。
触发特征¶
- 题面给 n, e, c 三元组;PEM 公钥文件;多个密文/多组公钥。
- 起手体检:
n位长 →factordb查已知分解 →e大小 → 密文间关系。
暴力分解N¶
- 小 N(<256 bit):yafu / msieve / SageMath
factor。 - 在线:factordb.com;
sympy.factorint试小因子。 - 试除结构:p = next_prime(2^k+small)(2019)、多素数 N(直接 sympy 分解)、受限数字素数(逐位从 LSB 确定剪枝,2018)。
- Fermat 分解:p,q 相邻(
q=next_prime(p))或q≈k·p(Coppersmith 广义,→ 下文);isqrt迭代。 - Pollard p-1:p-1 平滑 →
pow(a, lcm(1..B), n)gcd;变体 p+1(Williams)。 - batch GCD:多把公钥共享素因子(2016-2019 各赛常出);N1·N2 gcd。
- Coppersmith:已知素数高位(部分密钥泄露)→
f.small_roots()(SageMath);线性相关素数q=k·p+δ近似sqrt(k·n)后 small_roots(ASIS CTF 2018)。
p&q不当分解N¶
- p、q 相邻/相近:Fermat;p、q 来自同一 PRNG 种子(→ MT19937);
p = kp·B + tp基表示结构暴力(BACKDOOR 风格)。 - p=q:验签端
phi=(p-1)^2错误 → 解密失败泄露密文(2018)。 - 共享素数:batch GCD;ROCA 漏洞指纹检测(CVE-2017-15361,roca-detect)。
e和phi不互素¶
g = gcd(e,phi) > 1:令e' = e/g,算d' = e'^-1 mod phi,得m^g mod n后**在整数域开 g 次方**;g 与素因子结构耦合时逐素数域开根再 CRT 枚举组合(2018 cube root CRT)。- e=1 直接得 m;e 过大 → 对应 d 小(Wiener)。
经典攻击速查¶
| 场景 | 攻击 | 备注 |
|---|---|---|
| e 小 + m 小(小公钥指数攻击) | 直接开 e 次根 | m^e < n 时 |
| e 小 + 多密文同 m(Broadcast Attack) | Hastad 广播,CRT 合并开根 | 带线性 padding → CRT+Coppersmith(PlaidCTF 2017) |
| d 小(低解密指数攻击) | Wiener(e 很大) | 格/连分数;更大范围用 Boneh-Durfee |
| d < N^0.292 | Boneh-Durfee 格攻击 | Wiener 的格化推广,SA 系工具现成实现 |
| p±1 平滑 | Pollard p-1 / Williams p+1 | |
| 两密文线性相关 (e=3) | Franklin-Reiter 多项式 GCD(N1CTF 2018) | |
| LSB/parity oracle | 二分搜索 log2(n) 次(Rabin 版 PlaidCTF 2016;噪声版 SharifCTF 后处理纠错) | RSA parity oracle 即 LSB 奇偶 oracle |
| Manger oracle | 倍增+二分 ~128 次恢复 AES 密钥;OAEP 时序版(2018) | |
| 共模攻击 | 两密文同 n 不同 e 互素 → 扩展 GCD | |
| 模不互素(共享素因子) | batch GCD / gcd(n1,n2) |
多把公钥共享素因子(2016-2019 各赛常出) |
| dp/dq/qinv 泄露(dp&dq泄漏攻击) | 逐 k 迭代 (dp·e-1)/k+1 为素即 p(0CTF 2016) |
见例题节 |
| n=p²q | Schmidt-Samoa 类结构 | 2018+ |
| phi 的倍数泄露 | Miller-Rabin 平方根技巧分解(≥½ 成功率) | e·d-1 即 phi 倍数 |
| 同态解密绕过 | oracle 拒解 c → 解 c·r^e 再除 r(ectf 2016) |
|
| 小素因子 CRT | 试除 + 逐素域解 + CRT 合并(Hack the Vote 2016) | |
| Montgomery 时序(RSA侧信道攻击) | 泄露额外减法次数按位恢复密钥(DEF CON 2017) | |
| Known High Bits(已知素数高位) | Coppersmith small_roots |
p 高位泄露即够,X=低位上界 |
Rabin算法¶
c = m² mod n 的平方根体制(等价于 e=2 的 RSA):
- 解密:p、q 已知时开平方根得 ±r、±s 四个根,CRT 组合;正确明文需冗余校验(如后缀标记)。
- LSB/parity oracle:乘法同态
c·4 mod n使明文翻倍,oracle 告知奇偶 → 二分恢复 log2(n) 次(PlaidCTF 2016);含噪版后处理纠错(SharifCTF)。 - 变体:多项式素数 Rabin(2018);四根枚举绕"无冗余无唯一解"的实现缺陷。
- 与 RSA 关系:p=q 时 n=p² 在整数域直接开平方(验证绕过场景,→ 上文 p=q 条目)。
RSA签名伪造(RSA 数字签名族)¶
textbook RSA(无 padding)签名 = 私钥运算,天然带同态:
- 乘法同态伪造:目标消息被黑名单 → 分解为两因子分别签名后相乘(MMA CTF 2015)。
- 低指数伪造:e=3 构造带 PKCS#1 v1.5 前缀值的立方根,尾部垃圾吸收余数(Bleichenbacher,Google CTF 2017)。
- e=1 + 自定模数:验签端接受 (n,e) 时 e=1、
n = sig - pad(msg)(BackdoorCTF 2018)。 - CRT 故障攻击:
gcd(s⁻ᵉ·s' - 1, n)单条错误签名泄漏因子(Bellcore,CSAW CTF 2016)。
完整签名体系攻击(ElGamal/DSA/ECDSA)→ 电子签名。
中国剩余定理¶
- CRT 合并广播密文;多模数残余
r = flag mod f收集后 GF(2)[x] CRT(多项式域,2018)。 - Hensel 引理:p^k 上多项式根逐级提升(2018)。
- Rabin 四平方根 CRT 组合(2018)。
提取PEM文件信息¶
- 多文件比对 n/gcd;bit_length() 快速判位长;私钥文件直接 openssl rsa -in key.pem -text -noout。
私钥文件修复¶
- PEM 缺头/损坏:补
-----BEGIN...头、base64 对齐;DER 解析定位 asn1 断点。 - 部分字段泄露(dp/dq/qinv)→ 上述恢复攻击;PEM 中 p 泄露一半字节 → Coppersmith。
- 工具:RsaCtfTool
--attack全家桶、rsatool重建 PEM。
工具速查¶
python RsaCtfTool.py --publickey key.pub --uncipherfile flag.enc
sage: f = x*... ; f.small_roots(X=2^256, beta=0.5) # Coppersmith
转向¶
例题¶
Wiener 小私钥攻击(d < N^0.25,低解密指数攻击)¶
e 巨大(接近 n)即提示 d 很小,对 e/n 做连分数展开,在收敛分数中检验 phi=(e·d-1)/k 是否合法(反推 p+q 后判别式开方为整数):
def wiener_attack(e, n):
def continued_fraction(num, den):
cf = []
while den:
q, r = divmod(num, den)
cf.append(q); num, den = den, r
return cf
def convergents(cf):
convs = []; h0, h1, k0, k1 = 0, 1, 1, 0
for a in cf:
h0, h1 = h1, a*h1 + h0
k0, k1 = k1, a*k1 + k0
convs.append((h1, k1))
return convs
from math import isqrt
for k, d in convergents(continued_fraction(e, n)):
if k == 0 or (e*d - 1) % k: continue
phi = (e*d - 1) // k
s = n - phi + 1
disc = s*s - 4*n
if disc < 0: continue
t = isqrt(disc)
if t*t == disc: return d
return None
d = wiener_attack(e, n); m = pow(c, d, n)
也可直接 pip install owiener。原理:k/φ ≈ d/n 的连分数收敛分数中必含 k/d。
Wiener 打不动时(d ∈ [N^0.25, N^0.292)):升级 Boneh-Durfee 格攻击——把 ed ≡ 1 (mod φ) 的关系改写为模 e 多项式小根问题,构造二维格归约求小向量,界从 N^0.25 提升到 N^0.292;github.com/mimoo/RSA-and-LLL 有现成实现(SageMath),参数 m,t 调格维度。判据:Wiener 失败 + e 巨大 + d 明显小于 sqrt(n) 即上 Boneh-Durfee。
Fermat 分解(p、q 相邻)¶
q = next_prime(p) 时 |p-q| 小,从 sqrt(n) 向下找第一个整除的素数:
from sympy import prevprime, isqrt
root = isqrt(n)
p = prevprime(root + 1)
while n % p != 0:
p = prevprime(p)
q = n // p
多层变体:1024 层嵌套加密、每层素数位长递增——逐层倒序解密即可。
Coppersmith:线性相关素数 q ≈ k·p(ASIS CTF 2018)¶
已知 q ~ k·p(k 公开)时 q ~ sqrt(k·n),对误差项跑 small_roots——Fermat 的推广:
# SageMath
qbar = isqrt(4 * n) # k=4 时
R.<x> = PolynomialRing(Zmod(n))
f = x + qbar
roots = f.small_roots(X=2^200, beta=0.5)
q = qbar + int(roots[0]); p = n // q
Franklin-Reiter 相关消息攻击 e=3(N1CTF 2018)¶
同一明文 m 加已知线性 padding 加密两次(padding 差已知),多项式 GCD 直接出 m:
# SageMath
def franklin_reiter(n, pad1, pad2, c1, c2):
R.<X> = PolynomialRing(Zmod(n))
f1 = (X + pad1)^3 - c1
f2 = (X + pad2)^3 - c2
return -gcd(f1, f2).coefficients()[0]
CRT 部分私钥泄露恢复 dp/dq/qinv(0CTF 2016)¶
PEM 文件尾部泄露出 dp 即够:dp = d mod (p-1),枚举 k 检验 (dp·e-1)/k + 1 是否为素数,O(e) 秒出:
import gmpy2
for k in range(3, e):
p_candidate = (dp * e - 1) // k + 1
if gmpy2.is_prime(p_candidate):
p = p_candidate; break
# 同法从 dq 恢复 q;用 qinv·q % p == 1 校验
关键认知:CRT 指数泄露即等于整把私钥泄露。
多素数 RSA¶
N 为多个小素数之积时先分解再合成 phi: