RSA大礼包破解报告
一、题目描述及背景介绍
RSA 密码算法是使用最为广泛的公钥密码体制。该体制简单且易于实现,只需要选择 5 个参数即可(两个素数 p p p和 q q q、模数 N = p q N=pq N=pq,加密指数𝑒和解密指数𝑑)。设𝑚为待加密消息,RSA 体制破译相当于已知 𝑚 𝑒 ( m o d 𝑁 ) 𝑚^𝑒 \pmod 𝑁 me(modN),能否还原𝑚的数论问题。目前模数规模为 2048 比特的RSA 算法一般情况下是安全的,但是如果参数选取不当,同样存在被破译的可能。
有人制作了一个 RSA 加解密软件(采用的 RSA 体制的参数特点描述见密码背景部分)。已知该软件发送某个明文的所有参数和加密过程的全部数据(加密案例文件详见附件 3-1)。Alice 使用该软件发送了一个通关密语,且所有加密数据已经被截获,请问能否仅从加密数据恢复该通关密语及 RSA 体制参数?如能请给出原文和参数,如不能请给出已恢复部分并说明剩余部分不能恢复的理由?
1.1 RSA密码算法体制参数选取以及加解密过程
RSA 体制参数选取
- 每个使用者,任意选择两个大素数𝑝和𝑞,并求出其乘积 N = p q N=pq N=pq
- 令Font metrics not found for font: .,选择整数𝑒,使得Font metrics not found for font: .,并求出𝑒模𝜑(𝑁)的逆元𝑑,即Font metrics not found for font: ..
- 将数对(𝑒, 𝑁)公布为公钥,𝑑保存为私钥。
加解密过程
Bob 欲传递明文𝑚给 Alice,则 Bob 首先由公开途径找出 Alice 的公钥(𝑒, 𝑁),Bob 计算加密的信息𝑐为: 𝑐 ≡ 𝑚 𝑒 ( m o d 𝑁 ) 𝑐 ≡ 𝑚^𝑒 \pmod 𝑁 c≡me(modN)。
Bob 将密文𝑐传送给 Alice。随后 Alice 利用自己的私钥𝑑解密: 𝑐 d ≡ ( 𝑚 𝑒 ) 𝑑 ≡ 𝑚 𝑒 𝑑 ≡ 𝑚 ( m o d 𝑁 ) 𝑐^d ≡ (𝑚^𝑒 )^𝑑 ≡ 𝑚^{𝑒𝑑} ≡ 𝑚 \pmod 𝑁 cd≡(me)d≡med≡m(modN)。
1.2 注意点:
1) 模数𝑁 = 𝑝𝑞规模为 1024 比特,其中𝑝,𝑞为素数;
2) 素数𝑝由某一随机数发生器生成;
3) 素数𝑞可以随机选择,也可以由 2) 中的随机数发生器产生;
4) 可以对文本加密,每次加密最多 8 个明文字符;
5) 明文超过 8 个字符时,对明文分片,每个分片不超过 8 个字符;
6) 分片明文填充为 512 比特消息后再进行加密,填充规则为高位添加 64 比特标志位,随后加上 32 比特通信序号,再添加若干个 0,最后 64 比特为明文分片字符对应的 ASCII 码(注:填充方式参见加密案例,但注意每次通信的标志位可能变化);
7) 分片加密后发送一个加密帧数据,帧数据文件名称为 FrameXX,其中 XX 表示接收序号,该序号不一定等于通信序号;
8) 帧数据的数据格式如下,其中数据都是 16 进制表示,结构如下
1024 b i t 模数 N ∣ 1024 b i t 加密指数 e ∣ 1024 b i t 密文 m e ( m o d N ) 1024bit模数N | 1024bit加密指数e|1024bit密文m^e \pmod N 1024bit模数N∣1024bit加密指数e∣1024bit密文me(modN)
9) 由于 Alice 初次使用该软件,可能会重复发送某一明文分片。
1.3 研究现状
RSA 的安全性是基于大整数素因子分解的困难性,而大整数因子分解问题是数学上的著名难题。数域筛法是目前 RSA 攻击的首选算法。在 1999 年,一台 Cray 超级电脑用了 5 个月时间分解了 512 比特长的密钥。在 512 比特 RSA 算法破解 10 年之后,即 2009 年 12 月 9 日,768比特 RSA 算法即 232 数位数字的 RSA-768 被分解。分解一个 768 比特RSA 密钥所需时间是 512 位的数千倍,而 1024 比特所需时间则是 768比特的一千多倍,因此在短时间内 1024 比特仍然是安全的。除此之外,目前对于 RSA 算法的攻击主要有以下方式:选择密文攻击、公共模数攻击、低加密密指数攻击、低解密指数攻击、定时攻击等等,详细的 RSA 安全分析参见有关文献。
二、针对赛题的攻击
首先用现有的分解大整数的方法对所有的模数进行测试
2.1 费马分解法:
费马分解法用于p与q相近的n,注意到 ( p + q ) 2 − 4 n = ( p − q ) 2 (p+q)^2-4n=(p-q)^2 (p+q)2−4n=(p−q)2,那么我们可以得知 ( p + q ) / 2 (p+q)/2 (p+q)/2与 N \sqrt{N} N接近,,通过爆破这个差值能够容易地计算出p+q,从而分解n。
from Crypto.Util.number import *
from gmpy2 import iroot
nec = open(f"./Frame14",'r').read()
n = int(nec[:len(nec)//3],16)
e = int(nec[len(nec)//3:len(nec)//3*2],16)
c = int(nec[len(nec)//3*2:],16)
a = iroot(n,2)[0]+1
for i in range(a,a+100000000):
if iroot(i**2 - n,2)[1]:
b = iroot(i**2 - n,2)[0]
p = (i+b)
q = (i-b)
print(p)
print(q)
break
d = inverse(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)).split(b'\x00')[-1])
2.2 Pollard p-1分解
Pollard’s p − 1 算法由John Pollard在1974年提出。这个算法要求N一个因子是 p − 1 p-1 p−1光滑的,即 p − 1 p-1 p−1是一些小于 B B B的因子的乘积。令整数 a a a与 p p p互素,根据费马小定理:
a k ( p − 1 ) = 1 ( m o d p ) a^{k(p-1)}=1 \pmod p ak(p−1)=1(modp)
即 gcd ( a k ( p − 1 ) − 1 , n ) \gcd(a^{k(p-1)}-1,n) gcd(ak(p−1)−1,n)如果不是 1 1 1或者 N N N,则一定是 p p p的倍数。又因为 p − 1 p-1 p−1光滑,那么存在 M = ∏ q ≤ B q ⌊ log q B ⌋ M=\prod_{q\leq B}q^{\left \lfloor \log _q B\right \rfloor} M=∏q≤Bq⌊logqB⌋使得 ( p − 1 ) ∣ M (p-1)|M (p−1)∣M。在算法中,考虑 M = N ! M=N! M=N!既能满足条件又能方便计算。
Alg 1. Pollard p-1算法
Input: N N N
Output: p p p
-
a = 2 , x = 2 a=2,x=2 a=2,x=2
-
while True:
a = a x ( m o d N ) a=a^x \pmod N a=ax(modN)
r e s = gcd ( a − 1 , N ) res=\gcd(a-1,N) res=gcd(a−1,N)
if r e s ≠ 1 res \neq 1 res=1 and r e s ≠ N res \neq N res=N : return r e s res res
n = n + 1 n=n+1 n=n+1
这个算法的时间复杂度是 O ( B log B ⋅ log 2 n ) \mathcal{O}(B \log B \cdot \log^2 n) O(BlogB⋅log2n),选取更大的 B B B需要更长的运行时间,更可能成功分解 N N N。
# python2
from Crypto.Util.number import long_to_bytes
import gmpy2
nec = open(f"./Frame6",'r').read()
n = int(nec[:len(nec)//3],16)
e = int(nec[len(nec)//3:len(nec)//3*2],16)
c = int(nec[len(nec)//3*2:],16)
def Pollard_p_1(N):
a =