参考文章
Wiener’s attack
Crypto Classics: Wiener’s RSA Attack
中文参考1
中文参考2
Wener’s attack
重点知识:
连分数、渐进分数。
连分数
举个例子比较清晰,如图
那么e/N的依次每个渐进分数
描述:
Wiener 表示如果满足:d<(1/3)* n **(1/4)
那么一种基于连分数(一个数论当中的问题)的特殊攻击类型就可以危害 RSA 的安全。
思路如下:
在RSA中有式子:ed = 1%n 推到公式变换为:
**e/phi -k/d = 1/(dphi)**
因为p,q很大有phi接近与n e/n -k/d = 1/(d*n)
又因为d * n很大所以有e/n接近于k/d (e/n略大于k/d)
计算e/n的连分数展开,依次算出每一个渐进分数。因为e/n > k/d 所以e/n 的渐进分数覆盖了k/d。也就是说e/n的渐进分数里有等于k/d的分数。接着验证k,d是否满足条件。
算法过程:
- 通过对e/n进行连分数展开
- 然后依次求其渐进分数
- 遍历所有渐进分数如果找到了d的话,(ed-1)会整除phi,也就是存在k使得(ed-1)//k=phi,建立方程求p,q。( x2−(p+q)∗x+pq=0这个方程的解就是p,q)如果p*q==n则找到了d
搬运代码:
理解最重要!!!
import gmpy2
# numerator(n):分子, denominator(d):分母
def t_cf(n, d): # 将分数 x/y 转为连分数的形式
res = []
while d:
res.append(n // d)
n, d = d, n % d
return res
def cf(sub_res): # 得到渐进分数的分母和分子
n, d = 1, 0
for i in sub_res[::-1]: # 从后面往前循环
d, n = n, i * n + d
return d, n
def list_fraction(x, y): # 列出每个渐进分数
res = t_cf(x, y)
res = list(map(cf, (res[0:i] for i in range(1, len(res))))) # 将连分数的结果逐一截取以求渐进分数
return res
def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q(解二元一次方程)
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2
def wienerAttack(e, n):
for (d, k) in list_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("求解d失败")