【CTF-CRYPTO-教学-RSA】第十三节:知道e、p和q的变形表达式,解密c

基础: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) 变形

https://ctf2.dasctf.com/dashboard/practice/b9bbb32f-f186-458f-b90b-12440c0f6aea?tab=challenges&challenge=3562d281-e26a-4c92-8cc8-bb4e6d80111f

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

https://ctf2.dasctf.com/dashboard/practice/b9bbb32f-f186-458f-b90b-12440c0f6aea?tab=challenges&challenge=f2bf3ed1-1528-49eb-a71c-d10b383038f5

原题代码:

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 的某种对称组合
攻击思路
  1. 化简数学表达式:识别导数公式的本质(z = p² + q²)
  2. 利用代数恒等式 :
    • (p+q)² = p² + 2pq + q² = z + 2n
    • (p-q)² = p² - 2pq + q² = z - 2n
  3. 开平方恢复 p+q 和 p-q
  4. 恢复 p 和 q
  5. 计算私钥 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)变形

题目

https://ctf2.dasctf.com/dashboard/practice/b9bbb32f-f186-458f-b90b-12440c0f6aea?tab=challenges&challenge=fea238cd-4af6-4b76-b3d9-6a6e86e715e2

内容如下:

复制代码
(p-2)*(q-2)= 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45ed756cc6faac6e6f4686dfb56e7fcd361ac312d0f7110e76f8fee5cff75894e8a2f4e50ffd0ef9db7f0eb685a6b3038892a96b355ea1d154b77db6e97a3facd36dd8ee14b94cb98a21f4cea1412e7c72ea4cad530995ade3f5aae3444204dfc0d6ede436427
e= 0x2e43a6e5
n= 0x9360ce5eb573dcdb85af4cef9468a29323aa9d26f8cef9a2b004f3d9922c12c45f74b85c00db81fa34de4714a6a95b676618a3ea8155df7095056c079531233f3e80cc372263ccaf4d42e5b7aa637586b673e30820a2d7eba201691371e138e4b3e45eda7d04ff5b6a850dd6c5d5dcaab3588c8acc1b56794cbef1337664afd984d491d8134e3c1d661414278836b76e0de6a4e9a16f1c3f6abe86448dd065f317515d09888955eba578c5579381f59a5355584d1b2003c93660ada247f13db12aadc74a6801803b
c= 0x49c627fa815685ad85060c0891e2cd04b5cd722cd82cc809835cb43da79b21ce547f4139da69a67e201c5f4643ff91306b92ae7d1e3cc96a01e7074c7016058bf607038061fc3a99b6ac3ae1eaf6a3fddcc70303ed56281896183a4cd98c18e5f0378bf18d6a09c685c6fefdd0c0914b4b22e183ac5c88d5674b54141ef8291855bc394296b8031c0b0b6ec26889871137b91224321bb0d2a89ae1cf84eeba9fe459d0b8dff7fb1aadbae839956dfdfef5b0a8dbdfe8fd2613228e75f45195ee24cfa58b85a57e0f

解题过程

第一步:分析已知条件

题目给了四个参数(注意前两个是十六进制表示):

  1. X = (p-2)(q-2):泄露的表达式(大整数)
  2. e = 0x2e43a6e5:公钥指数
  3. n:模数(大整数)
  4. 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) 泄露攻击。

核心思路只有两步:

  1. 代数展开:把泄露的对称多项式展开,用公开的 n=pq 代入,解出 p+q
  2. 解二次方程:已知 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

掌握了这个通用套路,以后遇到类似"泄露奇怪表达式"的题目都能秒破!

相关推荐
print_Hyon8 小时前
【CTF-CRYPTO-教学-RSA】第七节:低加密指数攻击,已知n和e求c(e很小,m的e次方比n小,c可直接开方得到结果)
ctf
hengdonghui3 天前
Writeup 4 N1BOOK afr_1
ctf·伪协议·任意文件读取
hengdonghui3 天前
Writeup 4 N1BOOK 常见的搜集
ctf·信息搜集
hengdonghui4 天前
Writeup 4 N1BOOK SQL注入-1
ctf·sql注入·union·字符型注入
hengdonghui5 天前
Writeup 4 红帽杯 2021 WebsiteManger
web·ctf·ssrf·sql布尔盲注
hengdonghui7 天前
Writeup 4 2020 - 之江杯 - 异常的流量分析
wireshark·ctf·流量分析
hengdonghui7 天前
Writeup 4 津门杯 2021 Web hate_php
php·web·ctf·通配符
hengdonghui7 天前
Writeup 4 红帽杯 2021 Web Find_It
web·ctf·备份文件泄露
hengdonghui8 天前
Writeup 4 强网杯 2019 强网先锋打野
ctf·misc·zsteg