Williams p+1分解算法
它是对 RSA 模数 n = p*q
发起攻击的一种方法,核心思想是利用某一个因数(通常是 p)满足“p+1 是 B-平滑数”,也就是说 p+1
只由小素数构成(比如 2 × 3 × 5 × 7 × ...)这种情况,能高效地分解 n
。
🧠 核心思想解析
这个攻击基于:
-
Lucas 序列:是一种类似 Fibonacci 的递推序列,在特定模下有很强的结构性。
-
p+1 平滑假设:如果
p+1 = k
是 B-平滑的(即它的所有因子都小于某个界限 B),我们可以通过构造特定序列将其“暴力爆破”出来。
下面是代码展示:
from math import isqrt
from gmpy2 import gcd
from Cryptodome.Util.number import long_to_bytes
from itertools import count
from sympy import primerange
# 示例已知参数,待分解的n
n = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
# Lucas 序列(Williams p+1 用的)
def mlucas(v, a, n):
v1, v2 = v, (v ** 2 - 2) % n
for bit in bin(a)[3:]:
if bit == "0":
v1, v2 = (v1 ** 2 - 2) % n, (v1 * v2 - v) % n
else:
v1, v2 = (v1 * v2 - v) % n, (v2 ** 2 - 2) % n
return v1
# 素数生成器
def primegen():
yield from primerange(2, 10**6) # 生成到 10^6 的素数,够用了
# 整数对数:ilog(x, b) = 最大整数 l,使得 b^l <= x
def ilog(x, b):
l = 0
while x >= b:
x //= b
l += 1
return l
# Williams p+1 分解攻击
def attack(n):
for v in count(1): # 不断尝试新的 v
for p in primegen():
e = ilog(isqrt(n), p)
if e == 0:
break
for _ in range(e):
v = mlucas(v, p, n)
g = gcd(v - 2, n)
if 1 < g < n:
return int(g), int(n // g)
if g == n:
break
# 开始攻击
p1, q1 = attack(n)
print(f"p = {p1}")
print(f"q = {q1}")
Pollard’s p-1 分解算法
✅ 用在什么时候?
当你怀疑 p-1
很光滑,也就是 p-1
是由很多小素数乘出来的时候。
🧠 一句话原理
如果一个大整数 n = p × q
,而且某个因数 p
的 p-1
是平滑数(就是只包含小素数因子的数),那我们可以用指数运算和 GCD 把 p
从 n
里揪出来。
🧪 攻击的关键点:
如果我们设:
M也就是一个由小素数乘出来的数(B
是我们控制的界限),只要 p-1
能整除这个 M
,我们就有:
所以:
就是我们要找的因子!
下面是代码展示:
from math import gcd
from sympy import primerange
from math import isqrt
def pollard_p_minus_1(n, B=10**5): # B为素数表的上限,可以根据实际往上调
a = 2 # 通常选2作为基
for p in primerange(2, B):
e = int(isqrt(n).bit_length() / p.bit_length())
a = pow(a, pow(p, e), n)
g = gcd(a - 1, n)
if 1 < g < n:
return g, n // g
else:
return None
# 示例参数n:
n = 198962376391690981640415251545285153602734402721821058212203976095413910572270
result = pollard_p_minus_1(n)
if result:
p, q = result
print(f"[+] Success! p = {p}, q = {q}")
else:
print("[-] Failed. Try increasing B.")
如果没有找到,B的大小可以试试从 10^5(10万)开始,往上调,10^6,10^7,甚至 10^8,最多不要超过 10^9,不然计算会很慢