RSA 常见漏洞与真实 Frame 破译案例
这篇文章记录一次 RSA 破译实验。数据来自“RSA 加密体制破译题目”的真实附件,不使用自造小数据。
题目背景大致是:Alice 使用一个 RSA 加解密软件发送通关密语,所有加密帧已经被截获。我们要尽量从截获数据恢复明文和 RSA 参数。如果不能完全恢复,也要说明哪些部分已经恢复,哪些部分不能靠当前数据验证。
题目规定每个 Frame 文件结构如下:
1024-bit N | 1024-bit e | 1024-bit ciphertext |
也就是说,每个文件都是一个 3072-bit 的十六进制字符串,里面依次包含:
RSA 模数 N |
1. 先理解题目中的填充格式
题目还规定每次最多加密 8 个明文字符。明文分片会填充成 512 bit 后再加密。
填充结构是:
64-bit 标志位 | 32-bit 通信序号 | 若干个 0 | 最后 64-bit 明文分片 ASCII |
因此恢复出整数 m 后,不能直接把整块都当作字符串,而要解析:
def decode_padded_message(message: int) -> dict[str, str | int]: |
其中最重要的是:
sequence 通信序号 |
Frame 文件名中的编号是接收序号,不一定等于通信序号,所以拼接明文时要看填充里解析出的 sequence。
2. 解析 Frame 文件
每个 Frame 是 768 个十六进制字符:
256 hex chars for N |
解析代码如下:
from dataclasses import dataclass |
运行:
python work\experiment_04_rsa_challenge\01_parse_frame_files.py |
真实 VSCode 截图:

结果显示:
Frame count: 21 |
21 个帧只有 20 个唯一模数,说明至少存在一次模数复用。
3. 检查危险关系
RSA 不是看到 1024 bit 就直接去分解。正常 1024 bit RSA 很难直接分解。应该先检查参数之间有没有明显弱关系。
本实验检查三类关系:
相同模数 N |
代码:
def analyze_frame_relations(frames: list[Frame]) -> list[str]: |
运行:
python work\experiment_04_rsa_challenge\02_analyze_vulnerable_relations.py |
截图:

关键发现:
same modulus: Frame0 / Frame4, gcd(e1,e2)=1 |
这些关系对应三类攻击:
共模攻击 |
4. 共模攻击
适用条件:
同一个 N |
假设:
c1 = m^e1 mod N |
由扩展欧几里得可得:
a * e1 + b * e2 = 1 |
所以:
c1^a * c2^b = m^(a e1 + b e2) = m mod N |
代码中要注意负指数,负指数需要先求模逆:
_, a, b = egcd(left.e, right.e) |
本题中 Frame0 和 Frame4 满足条件,恢复:
seq=0 |
5. 共享素因子攻击
适用条件:
两个不同模数 N1、N2 共享素因子 |
如果:
N1 = p * q1 |
那么:
gcd(N1, N2) = p |
拿到 p 后,任一相关模数都能被分解:
q = N / p |
代码:
factor = math.gcd(left.n, right.n) |
本题中 Frame1 和 Frame18 共享 512-bit 因子,恢复:
Frame18: seq=10, chunk='m A to B' |
6. Håstad 广播攻击
适用条件:
同一明文 m |
如果收集到:
c_i = m^e mod N_i |
并且 N_i 两两互素,就能用 CRT 合并:
C = m^e mod product(N_i) |
当 m^e < product(N_i) 时,合并结果就是普通整数意义下的 m^e,对它开 e 次方即可恢复 m。
代码核心:
combined, _ = crt([(frame.c, frame.n) for frame in group]) |
本题中 e=5 的 5 个 Frame:
Frame3, Frame8, Frame12, Frame16, Frame20 |
恢复:
seq=1 |
注意:题目里也有 e=3 组,但不能看到 e=3 就直接认定成功。代码必须检查模数两两互素、CRT 后是否能精确开方,以及解出的填充是否合理。
7. 攻击脚本真实运行结果
运行:
python work\experiment_04_rsa_challenge\03_run_rsa_attacks.py |
真实 VSCode 截图:

恢复条目如下:
Frame0: seq=0, chunk='My secre', attack=common modulus (Frame0, Frame4) |
按通信序号去重拼接,得到已验证片段:
My secret is a fm A to B. Imagin |
这里必须强调:没有恢复出的 Frame 不应该靠语感补全。报告和博客都只写数学关系能证明恢复的部分。
8. 单元测试截图
实验还用附件 3-1 的加密案例做了解析校验,并确认附件 3-2 至少恢复非空片段。

9. 常见问题
9.1 直接尝试分解 1024-bit N
正常 1024-bit RSA 模数不是本实验里优先走的方向。先找共模、共享因子、小指数重复这些参数关系,效率更高,也更符合题目设置。
9.2 共模攻击忘记负指数
Bézout 系数可能是负数。负指数不能直接普通幂运算,要先求密文关于 N 的模逆。
9.3 广播攻击只看 e 的数量
有 e 个相同指数密文还不够,还要确认模数两两互素、明文相同、CRT 合并后能精确开方。
9.4 把 Frame 文件名当通信序号
题目明确说 FrameXX 是接收序号,不一定等于通信序号。通信序号在解出的填充块里。
9.5 把重复分片当作新内容
题目说明 Alice 初次使用软件,可能重复发送某一明文分片。因此同一个 chunk 出现在多个 Frame 中时,要按通信序号去重。
10. 小结
这次 RSA 破译流程可以概括为:
解析 Frame |
RSA 的危险往往不在公式本身,而在参数、随机性和填充使用错误。只要出现共模、共享素因子、小指数同明文重复发送,就会从“1024 bit 很难分解”变成“几行数论代码即可恢复部分明文”。
参考资料
- 首届全国高校密码数学挑战赛赛题三:RSA 加密体制破译
- Dan Boneh, Twenty Years of Attacks on the RSA Cryptosystem
- Don Coppersmith, Finding a Small Root of a Univariate Modular Equation
- Cryptopals Set 5 Challenge 39: https://www.cryptopals.com/sets/5/challenges/39