基础:p+q 和 pq
已知
bash
p+q = 10499790783038484569805597902684142859480193320728007213400678232071927076798651687119789844875553678691846010779762493711953914470123187073249367456081129311312900659899447998876519337555563418886210866861967752734977578142838309788531494412
p*q = 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45eda7d04ff5b6a850dd6c5d5dcaab3588c8acc1b56794cbef1337664afd984d491d8134e3c1d661414278836b76e0de6a4e9a16f1c3f6abe86448dd065f317515d09888955eba578c5579381f59a5355584d1b2003c93660ada247f13db12aadc74a6801803b
计算过程(Z3和SymPy 两种方法)
python
# -*- coding: utf-8 -*-
"""
已知 p+q 和 p*q, 分别用 z3 和 SymPy 求解 p, q
解方程: x^2 - (p+q)*x + (p*q) = 0
"""
import time
# 已知数据
S = 10499790783038484569805597902684142859480193320728007213400678232071927076798651687119789844875553678691846010779762493711953914470123187073249367456081129311312900659899447998876519337555563418886210866861967752734977578142838309788531494412
N = 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45eda7d04ff5b6a850dd6c5d5dcaab3588c8acc1b56794cbef1337664afd984d491d8134e3c1d661414278836b76e0de6a4e9a16f1c3f6abe86448dd065f317515d09888955eba578c5579381f59a5355584d1b2003c93660ada247f13db12aadc74a6801803b
print(f"p+q (S) 的十进制位数: {len(str(S))}")
print(f"p*q (N) 的十进制位数: {len(str(N))}")
print(f"p*q (N) 的十六进制位数: {len(hex(N))-2}")
print()
# ============================================================
# 方法 1: SymPy 求解
# ============================================================
print("=" * 60)
print("方法 1: 使用 SymPy 求解")
print("=" * 60)
from sympy import symbols, solve, sqrt, Integer, isprime
x = symbols('x')
t0 = time.time()
# 求解二次方程 x^2 - S*x + N = 0
roots = solve(x**2 - S*x + N, x)
t1 = time.time()
print(f"SymPy 求解用时: {t1 - t0:.4f} 秒")
print(f"解的个数: {len(roots)}")
if len(roots) >= 2:
p_sym = int(roots[0])
q_sym = int(roots[1])
print(f"\n由 SymPy 求得:")
print(f"p = {p_sym}")
print(f"q = {q_sym}")
print(f"\np 的十进制位数: {len(str(abs(p_sym)))}")
print(f"q 的十进制位数: {len(str(abs(q_sym)))}")
# 验证
print(f"\n验证:")
print(f" p + q == S ? {p_sym + q_sym == S}")
print(f" p * q == N ? {p_sym * q_sym == N}")
print(f" p 是素数? {isprime(p_sym)}")
print(f" q 是素数? {isprime(q_sym)}")
else:
print("SymPy 未能求出实数解")
print()
# ============================================================
# 方法 2: z3 求解
# ============================================================
print("=" * 60)
print("方法 2: 使用 z3 求解")
print("=" * 60)
from z3 import Int, Solver, And, sat
p_z3 = Int('p')
q_z3 = Int('q')
solver = Solver()
# 约束: p + q = S, p * q = N, p > 0, q > 0, p <= q (避免对称重复)
solver.add(p_z3 + q_z3 == S)
solver.add(p_z3 * q_z3 == N)
solver.add(p_z3 > 0)
solver.add(q_z3 > 0)
solver.add(p_z3 <= q_z3)
t0 = time.time()
result = solver.check()
t1 = time.time()
print(f"z3 求解用时: {t1 - t0:.4f} 秒")
print(f"z3 结果: {result}")
if result == sat:
model = solver.model()
p_val = model[p_z3].as_long()
q_val = model[q_z3].as_long()
print(f"\n由 z3 求得:")
print(f"p = {p_val}")
print(f"q = {q_val}")
print(f"\np 的十进制位数: {len(str(p_val))}")
print(f"q 的十进制位数: {len(str(q_val))}")
print(f"\n验证:")
print(f" p + q == S ? {p_val + q_val == S}")
print(f" p * q == N ? {p_val * q_val == N}")
else:
print("z3 未能求出解 (unsat)")
print()
# ============================================================
# 对比两种方法的结果
# ============================================================
print("=" * 60)
print("两种方法结果对比")
print("=" * 60)
if len(roots) >= 2 and result == sat:
print(f"SymPy p == z3 p ? {p_sym == p_val}")
print(f"SymPy q == z3 q ? {q_sym == q_val}")
一、p+q 和 (p+1)(q+1) 变形

bash
p+q : 0x1232fecb92adead91613e7d9ae5e36fe6bb765317d6ed38ad890b4073539a6231a6620584cea5730b5af83a3e80cf30141282c97be4400e33307573af6b25e2ea
(p+1)(q+1) : 0x5248becef1d925d45705a7302700d6a0ffe5877fddf9451a9c1181c4d82365806085fd86fbaab08b6fc66a967b2566d743c626547203b34ea3fdb1bc06dd3bb765fd8b919e3bd2cb15bc175c9498f9d9a0e216c2dde64d81255fa4c05a1ee619fc1fc505285a239e7bc655ec6605d9693078b800ee80931a7a0c84f33c851740
e : 0xe6b1bee47bd63f615c7d0a43c529d219
d : 0x2dde7fbaed477f6d62838d55b0d0964868cf6efb2c282a5f13e6008ce7317a24cb57aec49ef0d738919f47cdcd9677cd52ac2293ec5938aa198f962678b5cd0da344453f521a69b2ac03647cdd8339f4e38cec452d54e60698833d67f9315c02ddaa4c79ebaa902c605d7bda32ce970541b2d9a17d62b52df813b2fb0c5ab1a5
enc_flag : 0x50ae00623211ba6089ddfae21e204ab616f6c9d294e913550af3d66e85d0c0693ed53ed55c46d8cca1d7c2ad44839030df26b70f22a8567171a759b76fe5f07b3c5a6ec89117ed0a36c0950956b9cde880c575737f779143f921d745ac3bb0e379c05d9a3cc6bf0bea8aa91e4d5e752c7eb46b2e023edbc07d24a7c460a34a9a
解题过程
python
# ============================================================
# 已知 p+q 和 (p+1)(q+1) 恢复 n 解密
# ============================================================
#
# 已知: p+q, (p+1)(q+1), e, d, c (没有 n)
# 目标: 恢复 n,解密得到 flag
#
# 原理:
# (p+1)(q+1) = p*q + (p+q) + 1
# => n + (p+q) + 1
# => n = (p+1)(q+1) - (p+q) - 1
# => phi = n - (p+q) + 1 (用于验证 e*d)
# => p,q 是 x^2 - (p+q)*x + n = 0 的两根
# ============================================================
import math
def main():
# ---- 题目参数(十六进制直接写)----
p_plus_q = 0x1232fecb92adead91613e7d9ae5e36fe6bb765317d6ed38ad890b4073539a6231a6620584cea5730b5af83a3e80cf30141282c97be4400e33307573af6b25e2ea
pq_plus = 0x5248becef1d925d45705a7302700d6a0ffe5877fddf9451a9c1181c4d82365806085fd86fbaab08b6fc66a967b2566d743c626547203b34ea3fdb1bc06dd3bb765fd8b919e3bd2cb15bc175c9498f9d9a0e216c2dde64d81255fa4c05a1ee619fc1fc505285a239e7bc655ec6605d9693078b800ee80931a7a0c84f33c851740
e = 0xe6b1bee47bd63f615c7d0a43c529d219
d = 0x2dde7fbaed477f6d62838d55b0d0964868cf6efb2c282a5f13e6008ce7317a24cb57aec49ef0d738919f47cdcd9677cd52ac2293ec5938aa198f962678b5cd0da344453f521a69b2ac03647cdd8339f4e38cec452d54e60698833d67f9315c02ddaa4c79ebaa902c605d7bda32ce970541b2d9a17d62b52df813b2fb0c5ab1a5
c = 0x50ae00623211ba6089ddfae21e204ab616f6c9d294e913550af3d66e85d0c0693ed53ed55c46d8cca1d7c2ad44839030df26b70f22a8567171a759b76fe5f07b3c5a6ec89117ed0a36c0950956b9cde880c575737f779143f921d745ac3bb0e379c05d9a3cc6bf0bea8aa91e4d5e752c7eb46b2e023edbc07d24a7c460a34a9a
print("=== 作业5: 已知 p+q 和 (p+1)(q+1) 恢复 n 解密 ===")
print("已知: p+q, (p+1)(q+1), e, d, c(没有 n)")
print()
# ---- 第一步: 恢复 n ----
n = pq_plus - p_plus_q - 1
print(f"第一步: n = (p+1)(q+1) - (p+q) - 1")
print(f" n = {n}")
print(f" n (hex) = {hex(n)}")
# ---- 第二步: 验证 e*d ----
phi = n - p_plus_q + 1
print(f"\n第二步: φ(n) = n - (p+q) + 1 = {phi}")
print(f" 验证 e*d mod φ = {(e * d) % phi}")
# ---- 第三步: 恢复 p, q(可选)----
S = p_plus_q
disc = S * S - 4 * n
sqrt_disc = math.isqrt(disc)
assert sqrt_disc * sqrt_disc == disc, "判别式不是完全平方,参数有误"
p = (S + sqrt_disc) // 2
q = (S - sqrt_disc) // 2
print(f"\n第三步: 解 x^2 - (p+q)*x + n = 0")
print(f" p = {p}")
print(f" q = {q}")
print(f" 验证 p*q == n ? {p * q == n}")
# ---- 第四步: 解密 ----
m = pow(c, d, n)
print(f"\n第四步: m = c^d mod n")
print(f" m = {m}")
# ---- 第五步: 转换为字节串 ----
m_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(f"\n明文(字节串): {m_bytes}")
try:
decoded = m_bytes.decode('utf-8')
print(f"明文(字符串): {decoded}")
except UnicodeDecodeError:
print(f"明文(hex): {m_bytes.hex()}")
if __name__ == "__main__":
main()
运行结果:
bash
=== 作业5: 已知 p+q 和 (p+1)(q+1) 恢复 n 解密 ===
已知: p+q, (p+1)(q+1), e, d, c(没有 n)
第一步: n = (p+1)(q+1) - (p+q) - 1
n = 57781903220726608495841176404144399752917062064507427196457877637670900336948551690361309554998254116151157399251452194844720219023732542692019705315125895750034030052835547472397685327838976088928408560337016632351666472475413414704943529721199894711361841006444372063562252312744949278332373569559097979989
n (hex) = 0x5248becef1d925d45705a7302700d6a0ffe5877fddf9451a9c1181c4d82365806085fd86fbaab08b6fc66a967b2566d743c626547203b34ea3fdb1bc06dd3bb642cd9ed8735d2539b47d99c1aeb589f2e56bc3ab06f914d39c54644d068483e855bdbf8059b4b09320ce1bade536a9551df5ee850a4084e749971143d15f3455
第二步: φ(n) = n - (p+q) + 1 = 57781903220726608495841176404144399752917062064507427196457877637670900336948551690361309554998254116151157399251452194844720219023732542692019705315125880499322593171037446280190864496915273808563852336215519473747448764111037062952671133423280931846500856510292476004573976112720706136421408427842016465260
验证 e*d mod φ = 1
第三步: 解 x^2 - (p+q)*x + n = 0
p = 8228801334907462855397256098699556584084854642543205682719705217859576250443629616812386484797164506834582095674143447181804355696220642775619711451990971
q = 7021910101974335245794950722131367118195509913680915814438898999848788125908122655583911434165700354149914056221915541094395668546921268189522005629523759
验证 p*q == n ? True
第四步: m = c^d mod n
m = 218774971849330166322582124815285537132549320464078139075125103874907591916940895612102354183545981
明文(字节串): b'flag{cc7490e-78ab-11e9-b422-8ba97e5da1fd}'
明文(字符串): flag{cc7490e-78ab-11e9-b422-8ba97e5da1fd}
答案
flag{cc7490e-78ab-11e9-b422-8ba97e5da1fd}
二、知道n,p²+q²变形
什么是导数泄露攻击?
在某些 RSA 题目中,出题人可能通过数学技巧(如导数、反三角函数等)来泄露 p 和 q 的某种组合形式。
一旦我们能得到 p² + q² 的值,结合已知的 n = p × q,就可以通过代数恒等式轻松分解 n,恢复出 p 和 q,进而计算私钥 d 解密密文。
题目中的数学陷阱
看下面这道题的核心代码:
python
from sympy import Derivative
from fractions import Fraction
z = Fraction(1, Derivative(arctan( p ), p)) - Fraction(1, Derivative(arth(q), q))
这看起来很复杂,但其实是一个"数学陷阱"。让我们用微积分知识来化简它。
展开数学表达式:
z = 1 d d p arctan ( p ) − 1 d d q arth ( q ) z=\frac{1}{\displaystyle\frac{\mathrm{d}}{\mathrm{d}p}\arctan\\(p\\)} -\frac{1}{\displaystyle\frac{\mathrm{d}}{\mathrm{d}q}\operatorname{arth}(q)} z=dpdarctan(p)1−dqdarth(q)1
关键数学推导
arctan( p ) 的导数
arctan( p ) 是反正切函数,它的导数是:
d d p arctan ( p ) = 1 1 + p 2 \frac{d}{dp}\arctan( p ) = \frac{1}{1+p^2} dpdarctan(p)=1+p21
所以:
Derivative ( arctan ( p ) , p ) = 1 1 + p 2 \text{Derivative}(\arctan( p ), p) = \frac{1}{1+p^2} Derivative(arctan(p),p)=1+p21
取倒数后:
Fraction ( 1 , Derivative ( arctan ( p ) , p ) ) = 1 + p 2 \text{Fraction}(1, \text{Derivative}(\arctan( p ), p)) = 1 + p^2 Fraction(1,Derivative(arctan(p),p))=1+p2
arth(q) 的导数
arth(q) 是反双曲正切函数(inverse hyperbolic tangent),它的导数是:
d d q arth ( q ) = 1 1 − q 2 \frac{d}{dq}\text{arth}(q) = \frac{1}{1-q^2} dqdarth(q)=1−q21
所以:
Derivative ( arth ( q ) , q ) = 1 1 − q 2 \text{Derivative}(\text{arth}(q), q) = \frac{1}{1-q^2} Derivative(arth(q),q)=1−q21
取倒数后:
Fraction ( 1 , Derivative ( arth ( q ) , q ) ) = 1 − q 2 \text{Fraction}(1, \text{Derivative}(\text{arth}(q), q)) = 1 - q^2 Fraction(1,Derivative(arth(q),q))=1−q2
合并计算 z
z = ( 1 + p 2 ) − ( 1 − q 2 ) = p 2 + q 2 z = (1 + p^2) - (1 - q^2) = p^2 + q^2 z=(1+p2)−(1−q2)=p2+q2
结论:z 实际上就是 p² + q²!
简单例子
我们用 p=7, q=11 来演示整个过程,数字小到可以手算。
前置知识
已知:
- p = 7, q = 11
- n = p × q = 7 × 11 = 77
- e = 5(公钥指数)
- z = p² + q² = 49 + 121 = 170
现在假设攻击者只知道:n=77, z=170, e=5, c=某个密文,要恢复明文。
解题思路
核心公式:
- (p+q)² = p² + 2pq + q² = z + 2n
- (p-q)² = p² - 2pq + q² = z - 2n
第一步:计算 p+q 和 p-q
p + q = z + 2 n p + q = \sqrt{z + 2n} p+q=z+2n
p + q = 170 + 2 × 77 = 170 + 154 = 324 = 18 p + q = \sqrt{170 + 2 \times 77} = \sqrt{170 + 154} = \sqrt{324} = 18 p+q=170+2×77 =170+154 =324 =18
p − q = z − 2 n p - q = \sqrt{z - 2n} p−q=z−2n
p − q = 170 − 2 × 77 = 170 − 154 = 16 = 4 p - q = \sqrt{170 - 2 \times 77} = \sqrt{170 - 154} = \sqrt{16} = 4 p−q=170−2×77 =170−154 =16 =4
第二步:恢复 p 和 q
p = ( p + q ) + ( p − q ) 2 = 18 + 4 2 = 22 2 = 11 p = \frac{(p+q) + (p-q)}{2} = \frac{18 + 4}{2} = \frac{22}{2} = 11 p=2(p+q)+(p−q)=218+4=222=11
q = ( p + q ) − ( p − q ) 2 = 18 − 4 2 = 14 2 = 7 q = \frac{(p+q) - (p-q)}{2} = \frac{18 - 4}{2} = \frac{14}{2} = 7 q=2(p+q)−(p−q)=218−4=214=7
验证:p × q = 11 × 7 = 77 ✓
第三步:计算私钥并解密
ϕ ( n ) = ( p − 1 ) ( q − 1 ) = 10 × 6 = 60 \phi(n) = (p-1)(q-1) = 10 \times 6 = 60 ϕ(n)=(p−1)(q−1)=10×6=60
d = e − 1 m o d ϕ ( n ) = 5 − 1 m o d 60 d = e^{-1} \mod \phi(n) = 5^{-1} \mod 60 d=e−1modϕ(n)=5−1mod60
找 5 × ? ≡ 1 (mod 60):
- 5 × 1 = 5 → 5 mod 60 = 5 ✗
- 5 × 2 = 10 → 10 mod 60 = 10 ✗
- 5 × 5 = 25 → 25 mod 60 = 25 ✗
- 5 × 12 = 60 → 60 mod 60 = 0 ✗
- 5 × 13 = 65 → 65 mod 60 = 5 ✗
- 5 × 36 = 180 → 180 mod 60 = 0 ✗
- 5 × 37 = 185 → 185 mod 60 = 5 ✗
- 5 × 48 = 240 → 240 mod 60 = 0 ✗
- 5 × 49 = 245 → 245 mod 60 = 5 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
- 5 × 37 = 185 → 185 mod 60 = 5 ✗
- 5 × 49 = 245 → 245 mod 60 = 5 ✗
- 5 × 53 = 265 → 265 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
- 5 × 36 = 180 → 180 mod 60 = 0 ✗
- 5 × 37 = 185 → 185 mod 60 = 5 ✗
- 5 × 53 = 265 → 265 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
- 5 × 53 = 265 → 265 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
- 5 × 49 = 245 → 245 mod 60 = 5 ✗
- 5 × 53 = 265 → 265 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
使用扩展欧几里得算法或直接试:
- 5 × 37 = 185 → 185 = 3 × 60 + 5 → 5 mod 60 = 5 ✗
- 5 × 49 = 245 → 245 = 4 × 60 + 5 → 5 mod 60 = 5 ✗
- 5 × 53 = 265 → 265 = 4 × 60 + 25 → 25 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 = 4 × 60 + 55 → 55 mod 60 = 55 ✗
- 5 × 53 = 265 → 265 mod 60 = 25 ✗
- 5 × 59 = 295 → 295 mod 60 = 55 ✗
用 Python 快速计算:d = pow(5, -1, 60) = 49
验证:5 × 49 = 245 = 4 × 60 + 1 → 1 mod 60 = 1 ✓
所以 d = 49。
假设密文 c = pow(m, e, n) = pow(2, 5, 77) = 32
解密:m = pow(32, 49, 77)
计算过程(用 Python):pow(32, 49, 77) = 2
加密时 m = 2,解密后 m = 2,闭环成功!
代码实现
python
# ============================================================
# 利用导数泄露 p²+q² 分解 n 攻击
# ============================================================
#
# 场景:
# - 出题人通过数学公式泄露了 z = p² + q²
# - 已知 n = p × q, e, c
# - 目标: 利用 z 和 n 分解出 p, q,然后解密
#
# 原理:
# (p+q)² = p² + 2pq + q² = z + 2n
# (p-q)² = p² - 2pq + q² = z - 2n
# p = ((p+q) + (p-q)) / 2
# q = ((p+q) - (p-q)) / 2
# ============================================================
import math
from fractions import Fraction
from sympy import Derivative, arctan, arth
def compute_z_demo(p, q):
"""演示题目中的导数计算,验证 z = p² + q²"""
z = Fraction(1, Derivative(arctan( p ), p)) - Fraction(1, Derivative(arth(q), q))
return int(z)
def factor_n_from_z(z, n):
"""利用 z = p² + q² 和 n = p*q 分解 n"""
# 计算 (p+q)² 和 (p-q)²
sum_sq = z + 2 * n # (p+q)²
diff_sq = z - 2 * n # (p-q)²
# 检查是否为完全平方数
if sum_sq < 0:
raise ValueError("z + 2n 不是完全平方数,参数有误")
if diff_sq < 0:
raise ValueError("z - 2n 不是完全平方数,参数有误")
sqrt_sum = math.isqrt(sum_sq)
sqrt_diff = math.isqrt(diff_sq)
if sqrt_sum * sqrt_sum != sum_sq:
raise ValueError("z + 2n 不是完全平方数")
if sqrt_diff * sqrt_diff != diff_sq:
raise ValueError("z - 2n 不是完全平方数")
p_plus_q = sqrt_sum # p + q
p_minus_q = sqrt_diff # |p - q|
# 恢复 p 和 q
p = (p_plus_q + p_minus_q) // 2
q = (p_plus_q - p_minus_q) // 2
# 确保 p >= q
if p < q:
p, q = q, p
return p, q
def main():
# ---- 用小例子演示 ----
p_original = 7
q_original = 11
e = 5
# 计算题目中的 z
z = compute_z_demo(p_original, q_original)
n = p_original * q_original
print("=== 演示:利用导数泄露 p²+q² 分解 n ===")
print(f"原始 p = {p_original}, q = {q_original}")
print(f"n = p × q = {n}")
print(f"z = p² + q² = {p_original}² + {q_original}² = {z}")
print()
# ---- 验证 z = p² + q² ----
print(f"验证: Derivative(arctan({p_original}), {p_original}) = 1/(1+{p_original}²) = 1/(1+{p_original**2}) = 1/{1+p_original**2}")
print(f" Fraction(1, 1/{1+p_original**2}) = {1+p_original**2}")
print(f" Derivative(arth({q_original}), {q_original}) = 1/(1-{q_original}²) = 1/(1-{q_original**2}) = 1/{1-q_original**2}")
print(f" Fraction(1, 1/{1-q_original**2}) = {1-q_original**2}")
print(f" z = ({1+p_original**2}) - ({1-q_original**2}) = {z} = {p_original**2 + q_original**2} ✓")
print()
# ---- 分解 n ----
p_recovered, q_recovered = factor_n_from_z(z, n)
print("=== 分解过程 ===")
print(f"(p+q)² = z + 2n = {z} + 2×{n} = {z + 2*n}")
print(f"p+q = √{z + 2*n} = {math.isqrt(z + 2*n)}")
print(f"(p-q)² = z - 2n = {z} - 2×{n} = {z - 2*n}")
print(f"|p-q| = √{z - 2*n} = {math.isqrt(z - 2*n)}")
print()
print(f"p = ((p+q) + |p-q|) / 2 = ({math.isqrt(z + 2*n)} + {math.isqrt(z - 2*n)}) / 2 = {p_recovered}")
print(f"q = ((p+q) - |p-q|) / 2 = ({math.isqrt(z + 2*n)} - {math.isqrt(z - 2*n)}) / 2 = {q_recovered}")
print(f"验证: p×q = {p_recovered}×{q_recovered} = {p_recovered * q_recovered} = n? {p_recovered * q_recovered == n}")
print()
# ---- 计算私钥并解密 ----
phi = (p_recovered - 1) * (q_recovered - 1)
d = pow(e, -1, phi)
print("=== 解密过程 ===")
print(f"φ(n) = (p-1)(q-1) = ({p_recovered}-1)({q_recovered}-1) = {phi}")
print(f"d = e^(-1) mod φ = {e}^(-1) mod {phi} = {d}")
print(f"验证: e*d mod φ = {(e * d) % phi}")
# 加密明文 m=2
m = 2
c = pow(m, e, n)
print(f"\n加密: c = pow({m}, {e}, {n}) = {c}")
# 解密
m_dec = pow(c, d, n)
print(f"解密: m = pow({c}, {d}, {n}) = {m_dec}")
print(f"验证: 明文 m = {m}, 解密结果 = {m_dec}, 相等? {m == m_dec}")
if __name__ == "__main__":
main()
运行结果:
bash
=== 演示:利用导数泄露 p²+q² 分解 n ===
原始 p = 7, q = 11
n = p × q = 77
z = p² + q² = 7² + 11² = 170
验证: Derivative(arctan(7), 7) = 1/(1+7²) = 1/(1+49) = 1/50
Fraction(1, 1/50) = 50
Derivative(arth(11), 11) = 1/(1-11²) = 1/(1-121) = 1/(-120)
Fraction(1, 1/(-120)) = -120
z = (50) - (-120) = 170 = 49 + 121 ✓
=== 分解过程 ===
(p+q)² = z + 2n = 170 + 2×77 = 324
p+q = √324 = 18
(p-q)² = z - 2n = 170 - 2×77 = 16
|p-q| = √16 = 4
p = ((p+q) + |p-q|) / 2 = (18 + 4) / 2 = 11
q = ((p+q) - |p-q|) / 2 = (18 - 4) / 2 = 7
验证: p×q = 11×7 = 77 = n? True
=== 解密过程 ===
φ(n) = (p-1)(q-1) = (11-1)(7-1) = 60
d = e^(-1) mod φ = 5^(-1) mod 60 = 49
验证: e*d mod φ = 1
加密: c = pow(2, 5, 77) = 32
解密: m = pow(32, 49, 77) = 2
验证: 明文 m = 2, 解密结果 = 2, 相等? True
作业:BJDCTF2020 easyrsa

原题代码:
python
from Crypto.Util.number import getPrime,bytes_to_long
from sympy import Derivative
from fractions import Fraction
from secret import flag
p=getPrime(1024)
q=getPrime(1024)
e=65537
n=p*q
z=Fraction(1,Derivative(arctan( p ),p))-Fraction(1,Derivative(arth(q),q))
m=bytes_to_long(flag)
c=pow(m,e,n)
print(c,z,n)
题目给出的输出:
c = 7922547866857761459807491502654216283012776177789511549350672958101810281348402284098310147796549430689253803510994877420135537268549410652654479620858691324110367182025648788407041599943091386227543182157746202947099572389676084392706406084307657000104665696654409155006313203957292885743791715198781974205578654792123191584957665293208390453748369182333152809882312453359706147808198922916762773721726681588977103877454119043744889164529383188077499194932909643918696646876907327364751380953182517883134591810800848971719184808713694342985458103006676013451912221080252735948993692674899399826084848622145815461035
z = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482
n = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
解题过程
第一步:理解 z 的含义
虽然题目中通过复杂的导数公式计算 z,但我们已经证明:
z = p 2 + q 2 z = p^2 + q^2 z=p2+q2
所以我们已知:
- n = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
- z = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482
第二步:分解 n
利用公式:
- (p+q)² = z + 2n
- (p-q)² = z - 2n
计算:
-
sum_sq = z + 2n = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482 + 2 × 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
-
diff_sq = z - 2n = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482 - 2 × 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
对 sum_sq 和 diff_sq 开平方,即可得到 p+q 和 p-q,进而恢复 p 和 q。
第三步:具体实现代码
python
# ============================================================
# [BJDCTF2020] easyrsa 解题代码
# ============================================================
import math
def main():
# ---- 题目参数 ----
c = 7922547866857761459807491502654216283012776177789511549350672958101810281348402284098310147796549430689253803510994877420135537268549410652654479620858691324110367182025648788407041599943091386227543182157746202947099572389676084392706406084307657000104665696654409155006313203957292885743791715198781974205578654792123191584957665293208390453748369182333152809882312453359706147808198922916762773721726681588977103877454119043744889164529383188077499194932909643918696646876907327364751380953182517883134591810800848971719184808713694342985458103006676013451912221080252735948993692674899399826084848622145815461035
z = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482
n = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
e = 65537
print("=== [BJDCTF2020] easyrsa 解题 ===")
print(f"已知: n, z, c, e")
print(f"核心原理: z = p² + q²")
print()
# ---- 第一步: 计算 (p+q)² 和 (p-q)² ----
sum_sq = z + 2 * n # (p+q)²
diff_sq = z - 2 * n # (p-q)²
print(f"第一步: 计算 (p+q)² 和 (p-q)²")
print(f" (p+q)² = z + 2n = {sum_sq}")
print(f" (p-q)² = z - 2n = {diff_sq}")
print()
# ---- 第二步: 开平方求 p+q 和 p-q ----
sqrt_sum = math.isqrt(sum_sq)
sqrt_diff = math.isqrt(diff_sq)
print(f"第二步: 开平方")
print(f" p+q = √sum_sq = {sqrt_sum}")
print(f" |p-q| = √diff_sq = {sqrt_diff}")
print()
# ---- 第三步: 恢复 p 和 q ----
p_plus_q = sqrt_sum
p_minus_q = sqrt_diff
p = (p_plus_q + p_minus_q) // 2
q = (p_plus_q - p_minus_q) // 2
# 确保 p >= q
if p < q:
p, q = q, p
print(f"第三步: 恢复 p 和 q")
print(f" p = ((p+q) + |p-q|) / 2 = {p}")
print(f" q = ((p+q) - |p-q|) / 2 = {q}")
print(f" 验证: p×q == n? {p * q == n}")
print()
# ---- 第四步: 计算私钥 d ----
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
print(f"第四步: 计算私钥")
print(f" φ(n) = (p-1)(q-1) = {phi}")
print(f" d = e^(-1) mod φ = {d}")
print(f" 验证: e*d mod φ = {(e * d) % phi}")
print()
# ---- 第五步: 解密密文 ----
m = pow(c, d, n)
print(f"第五步: 解密密文")
print(f" m = c^d mod n = {m}")
print()
# ---- 第六步: 转换为字节串 ----
m_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(f"第六步: 转换为字节串")
print(f" 明文(字节串): {m_bytes}")
try:
decoded = m_bytes.decode('utf-8')
print(f" 明文(字符串): {decoded}")
except UnicodeDecodeError:
print(f" 明文(hex): {m_bytes.hex()}")
if __name__ == "__main__":
main()
运行结果
bash
=== [BJDCTF2020] easyrsa 解题 ===
已知: n, z, c, e
核心原理: z = p² + q²
第一步: 计算 (p+q)² 和 (p-q)²
(p+q)² = z + 2n = 62737239000297000494285002890834807471836283263808923956899738257642284095541235214174584467428329086916278932775843067715515548224439590741302686539449073964345427944320578862517477382054822233202109461022378027816171755471884222576542668714881667542271913850584891349137129512464574210132669815728763992195175534009360634240730310990373609874494463173791158048143031036309210293298949433608626035407945158692601526824533988148282365495793239682802328934157794889951330620152865004723827247735392220110720067684386033703749985934819639763647958913273456065426350323126029890577221003125649300615076516449959353283364
(p-q)² = z - 2n = 1494258354949418840658242853535742668679566268221116188710796462035834691027397977591282277151135167631873936399195598884769397796249798866467650575648528440646438508110296664141083102172290815794805658103367773807032358416963712231012577899042093972954578806874341936928128415681288334039064040363183606554248159640954927873200017366636855173997155184680057094991317421335913102478342503510061181342718817501712763704181265328000927616913762307045902817338601746121263177055328997152717144070721247021040233396164704775525602017027019433784047787343062577447154835455970820543642081333750219295205789379457371705600
第二步: 开平方
p+q = √sum_sq = 250474028594377426111821218884061933467907597574578255066146260367094595399741196827532923836761733594976933366636149201492628708413319929361097646526652140204561542573663223469009835925309935515892458499676903149172534494580503088868430625144808189083708827363335045028702993282231537893799541685169911232442
|p-q| = √diff_sq = 38655638074534726798492077075663447529685791965622785744291599744172599600277952500074987075247145202422406135863097610906456502129057888930841082426037785952636937542696842531871082637806760303859828128204411356302639111523367696324607828786231529865293956576176379165535334571113969283794819938252097174160
第三步: 恢复 p 和 q
p = ((p+q) + |p-q|) / 2 = 144564833334456076455156647979862690498796694770100520405218930055633597500009574663803955456004439398699669751249623406199542605271188909145969364476344963078599240058180033000440459281558347909876143313940657252737586803051935392596519226965519859474501391969755712097119163926672753588797180811711004203301
q = ((p+q) - |p-q|) / 2 = 105909195259921349656664570904199242969110902804477734660927330311460997899731622163728968380757294196277263615386525795293086103142131020215128282050307177125962302515483190468569376643751587606016315185736245896434947691528567696271911398179288329609207435393579332931583829355558784305002360873458907029141
验证: p×q == n? True
第四步: 计算私钥
φ(n) = (p-1)(q-1) = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096409981369563507049165770752187050126741656419399703196983221782143376229702305410535697108289679544851702745257413452031503577730761306549414578008882678146145752955318200720778423767689990857807756527500072378429082883561484867651993597547156337790182911090044554179722479691737165743232436168140082455584162000
d = e^(-1) mod φ = 10831551469554966186233544037602170684245380574268860030838732690737665060160213122499306585402184334321460218090570529795644881969164714247237635257280805089723285877521299128147165511056630936099730814343565434094022227921076394028030567200915459927093965706267436383064750167276730191949600752891686257380599908730067608030910709284837451763891515160105574330990046955086371269934358516213142633057698971639624656525585394336510336283583576560683198862268482962321894813205642891356631600145507597217200070393148198513798517550153406732550416960746529503036296730483696059524366811144124986323305913404381810337473
验证: e*d mod φ = 1
第五步: 解密密文
m = c^d mod n = 553104726995033004273124432204499589917519585497340626925465251997165623198835101451016074240381
第六步: 转换为字节串
明文(字节串): b'BJD{Advanced_mathematics_is_too_hard!!!}'
明文(字符串): BJD{Advanced_mathematics_is_too_hard!!!}
答案
BJD{Advanced_mathematics_is_too_hard!!!}
总结
攻击条件
- 攻击者已知 n、e、c
- 出题人通过某种方式泄露了 z = p² + q²
- 或者更一般地,泄露了 p 和 q 的某种对称组合
攻击思路
- 化简数学表达式:识别导数公式的本质(z = p² + q²)
- 利用代数恒等式 :
- (p+q)² = p² + 2pq + q² = z + 2n
- (p-q)² = p² - 2pq + q² = z - 2n
- 开平方恢复 p+q 和 p-q
- 恢复 p 和 q
- 计算私钥 d 并解密
拓展思考
类似的攻击还可能利用:
- 泄露 p³ + q³:利用 p³ + q³ = (p+q)(p² - pq + q²)
- 泄露 1/p + 1/q:利用 1/p + 1/q = (p+q)/(pq)
- 其他对称多项式组合
核心思想:只要能通过某种方式得到 p+q 和 p×q(或 p²+q²),就可以分解 n。
常见的数学恒等式总结
| 已知条件 | 可恢复的信息 |
|---|---|
| p+q, p×q | p 和 q(直接因式分解) |
| p²+q², p×q | p+q = √(p²+q²+2pq),p-q = √(p²+q²-2pq) |
| p+q, p²+q² | p×q = ((p+q)² - (p²+q²))/2 |
| p-q, p×q | p+q = √((p-q)² + 4pq) |
三、知道n,(p-2)(q-2)变形
题目

内容如下:
(p-2)*(q-2)= 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45ed756cc6faac6e6f4686dfb56e7fcd361ac312d0f7110e76f8fee5cff75894e8a2f4e50ffd0ef9db7f0eb685a6b3038892a96b355ea1d154b77db6e97a3facd36dd8ee14b94cb98a21f4cea1412e7c72ea4cad530995ade3f5aae3444204dfc0d6ede436427
e= 0x2e43a6e5
n= 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45eda7d04ff5b6a850dd6c5d5dcaab3588c8acc1b56794cbef1337664afd984d491d8134e3c1d661414278836b76e0de6a4e9a16f1c3f6abe86448dd065f317515d09888955eba578c5579381f59a5355584d1b2003c93660ada247f13db12aadc74a6801803b
c= 0x49c627fa815685ad85060c0891e2cd04b5cd722cd82cc809835cb43da79b21ce547f4139da69a67e201c5f4643ff91306b92ae7d1e3cc96a01e7074c7016058bf607038061fc3a99b6ac3ae1eaf6a3fddcc70303ed56281896183a4cd98c18e5f0378bf18d6a09c685c6fefdd0c0914b4b22e183ac5c88d5674b54141ef8291855bc394296b8031c0b0b6ec26889871137b91224321bb0d2a89ae1cf84eeba9fe459d0b8dff7fb1aadbae839956dfdfef5b0a8dbdfe8fd2613228e75f45195ee24cfa58b85a57e0f
解题过程
第一步:分析已知条件
题目给了四个参数(注意前两个是十六进制表示):
- X = (p-2)(q-2):泄露的表达式(大整数)
- e = 0x2e43a6e5:公钥指数
- n:模数(大整数)
- c:密文(大整数)
这正好对应我们刚刚讲的攻击场景------已知 (p-2)(q-2) 和 n,反推出 p+q,再解二次方程分解 n。
第二步:套用公式一步步计算
第一步:由 X 和 n 推出 p+q
S = p+q = (n - X + 4) / 2
注意 n 和 X 的十六进制长得几乎一样,只差后面一小部分------这正好印证了我们的展开式:
X = n - 2(p+q) + 4,所以 n 和 X 的高位几乎相同,差异就在低位部分。
第二步:解二次方程恢复 p、q
判别式 D = S² - 4n,开方后:
p = (S + sqrt(D)) / 2
q = (S - sqrt(D)) / 2
第三步:求私钥 d 并解密
φ(n) = (p-1)(q-1)
d = e⁻¹ mod φ(n)
m = cᵈ mod n
具体实现代码
python
# ============================================================
# 作业18: 已知 (p-2)(q-2) 的 RSA 攻击
# ============================================================
#
# 已知: X=(p-2)(q-2), n, e, c(全是十六进制大整数)
# 目标: 分解 n,恢复私钥,解密密文得到 flag
#
# 攻击链:
# 1. X = (p-2)(q-2) = n - 2(p+q) + 4
# => S = p+q = (n - X + 4) / 2
# 2. D = S² - 4n = (p-q)² => sqrt(D) = |p-q|
# 3. p = (S + sqrt(D))/2, q = (S - sqrt(D))/2
# 4. φ(n) = (p-1)(q-1), d = e⁻¹ mod φ(n)
# 5. m = cᵈ mod n
# ============================================================
import math
def main():
# ---- 题目参数(十六进制)----
X = 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45ed756cc6faac6e6f4686dfb56e7fcd361ac312d0f7110e76f8fee5cff75894e8a2f4e50ffd0ef9db7f0eb685a6b3038892a96b355ea1d154b77db6e97a3facd36dd8ee14b94cb98a21f4cea1412e7c72ea4cad530995ade3f5aae3444204dfc0d6ede436427
e = 0x2e43a6e5
n = 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45eda7d04ff5b6a850dd6c5d5dcaab3588c8acc1b56794cbef1337664afd984d491d8134e3c1d661414278836b76e0de6a4e9a16f1c3f6abe86448dd065f317515d09888955eba578c5579381f59a5355584d1b2003c93660ada247f13db12aadc74a6801803b
c = 0x49c627fa815685ad85060c0891e2cd04b5cd722cd82cc809835cb43da79b21ce547f4139da69a67e201c5f4643ff91306b92ae7d1e3cc96a01e7074c7016058bf607038061fc3a99b6ac3ae1eaf6a3fddcc70303ed56281896183a4cd98c18e5f0378bf18d6a09c685c6fefdd0c0914b4b22e183ac5c88d5674b54141ef8291855bc394296b8031c0b0b6ec26889871137b91224321bb0d2a89ae1cf84eeba9fe459d0b8dff7fb1aadbae839956dfdfef5b0a8dbdfe8fd2613228e75f45195ee24cfa58b85a57e0f
print("=== 作业18: 已知 (p-2)(q-2) 的 RSA 攻击 ===")
print(f"已知: X=(p-2)(q-2), n, e={hex(e)}, c")
print()
# ---- 第一步: 计算 S = p+q ----
numerator = n - X + 4
assert numerator % 2 == 0, "n - X + 4 不是偶数!"
S = numerator // 2
print(f"【第一步】计算 S = p+q")
print(f" 公式: S = (n - X + 4) / 2")
print(f" n 和 X 的高位几乎相同,说明 n - X 是一个相对较小的数")
print(f" S = p+q = {S}")
print(f" S 的十进制位数: {len(str(S))} 位")
print()
# ---- 第二步: 计算判别式 D ----
D = S * S - 4 * n
print(f"【第二步】计算判别式 D = (p+q)² - 4n = (p-q)²")
print(f" D 的十进制位数: {len(str(D))} 位")
print()
# ---- 第三步: 开平方 ----
sqrt_D = math.isqrt(D)
assert sqrt_D * sqrt_D == D, "D 不是完全平方数,攻击失败!"
print(f"【第三步】开平方 sqrt(D) = |p-q|")
print(f" sqrt(D) 的位数: {len(str(sqrt_D))} 位")
print(f" 验证: sqrt(D)² == D? {sqrt_D * sqrt_D == D} ✓")
print()
# ---- 第四步: 恢复 p 和 q ----
p = (S + sqrt_D) // 2
q = (S - sqrt_D) // 2
print(f"【第四步】恢复 p 和 q")
print(f" p = (S + sqrt(D)) / 2 = {p}")
print(f" q = (S - sqrt(D)) / 2 = {q}")
print(f" 验证 p*q == n? {p * q == n} ✓")
print()
# ---- 第五步: 计算 φ(n) 和私钥 d ----
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
print(f"【第五步】计算 φ(n) 和 d")
print(f" φ(n) = (p-1)(q-1) = {phi}")
print(f" d = e⁻¹ mod φ(n) = {d}")
print(f" 验证 (e*d) mod φ = {(e * d) % phi} ✓")
print()
# ---- 第六步: 解密 ----
m = pow(c, d, n)
print(f"【第六步】解密 m = cᵈ mod n")
print(f" m = {m}")
print()
# ---- 第七步: 转换为字节串 ----
m_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(f"明文(字节串): {m_bytes}")
try:
decoded = m_bytes.decode('utf-8')
print(f"明文(字符串): {decoded}")
except UnicodeDecodeError:
print(f"解码 UTF-8 失败,尝试 Latin-1...")
decoded = m_bytes.decode('latin-1')
print(f"明文(Latin-1): {decoded}")
if __name__ == "__main__":
main()
运行结果
bash
=== 作业18: 已知 (p-2)(q-2) 的 RSA 攻击 ===
已知: X=(p-2)(q-2), n, e=0x2e43a6e5, c
【第一步】计算 S = p+q
公式: S = (n - X + 4) / 2
n 和 X 的高位几乎相同,说明 n - X 是一个相对较小的数
S = p+q = 10499790783038484569805597902684142859480193320728007213400678232071927076798651687119789844875553678691846010779762493711953914470123187073249367456081129311312900659899447998876519337555563418886210866861967752734977578142838309788531494412
S 的十进制位数: 155 位
【第二步】计算判别式 D = (p+q)² - 4n = (p-q)²
D 的十进制位数: 309 位
【第三步】开平方 sqrt(D) = |p-q|
sqrt(D) 的位数: 155 位
验证: sqrt(D)² == D? True ✓
【第四步】恢复 p 和 q
p = (S + sqrt(D)) / 2 = 6651526941820305978582886293023819635729965090384433203534015396755618141702981611957151017946565029381016170836461974040479728255957381491315921447803996869731551811249004117113813017281783648425277705134296690152186280760860822535042221363
q = (S - sqrt(D)) / 2 = 3848263841218178591222711609660323223750228230343574009866662835316308935095670075162638826928988649310829839943300519671474186214165805581933446008277132441581348848650443881762706320273779770460933161727671062582791297381977487253489273049
验证 p*q == n? True ✓
【第五步】计算 φ(n) 和 d
φ(n) = (p-1)(q-1) = 25596830619095614994582907348365993410457361758532261837898233735137541540959577791487588263956416147123689064793518552204806688145846240061347071992296603301498492382073597330456564344396234446432266546498530829234057381580302710366901023830231676405982674462408342946975527079195886049517372351887521456008105885220129525696855780348673085489936008129380896153300123037416400870405261415238709109548211942463137109092691033062526458108644597349976327958921903865666493040476451376
d = e⁻¹ mod φ(n) = 5059871394823378908576839887150840568976322700329662340185041653740497344737597904606167820103143892843842911126073734240770875429883206478168986647756098571636945409470397606987565883196878736783139823017348571207816930544137422208432614514429471870669163089868761714281711685081740682300257440521687239135594660811523675717190045046725245679717850506416859674603353860331425675152703598706436728645235526970374415991604239320033692088053764083500234069805097250616261498084095949
验证 (e*d) mod φ = 1 ✓
【第六步】解密 m = cᵈ mod n
m = 218774971804085528558358877857769714277243423639251379985345244559099868774996124881333585632769591
明文(字节串): b'flag is :31e7957b168706db1acb124c3589e367'
明文(字符串): flag is :31e7957b168706db1acb124c3589e367
答案
flag is :31e7957b168706db1acb124c3589e367
(即 31e7957b168706db1acb124c3589e367)
小结
本节我们学习了一种新的 RSA 攻击姿势------已知 (p-2)(q-2) 泄露攻击。
核心思路只有两步:
- 代数展开:把泄露的对称多项式展开,用公开的 n=pq 代入,解出 p+q
- 解二次方程:已知 p+q 和 pq,用判别式开方后恢复 p 和 q
这告诉我们一个重要的 CTF 经验:不要被题目泄露的"奇怪表达式"吓到,先把它展开看看再说! 只要是 p、q 的对称多项式,几乎都能和 n=pq 配合推出有用的关系。
类似的推广:
| 泄露表达式 | 展开后含有的项 | 能推出什么 |
|---|---|---|
| (p+1)(q+1) | pq + (p+q) + 1 | p+q = 表达式 - n - 1 |
| (p-1)(q-1) | pq - (p+q) + 1 | p+q = n - 表达式 + 1 |
| (p-2)(q-2) | pq - 2(p+q) + 4 | p+q = (n - 表达式 + 4) / 2 |
| (p+k)(q+k) (任意常数k) | pq + k(p+q) + k² | p+q = (表达式 - n - k²) / k |
掌握了这个通用套路,以后遇到类似"泄露奇怪表达式"的题目都能秒破!