RSA大礼包
题目描述
RSA加密算法中包含了五个参数,包括两个素数p和q、模数N=pq、加密指数e和解密指数d。如果RSA加密的参数选取不当,同样存在被破解的可能。已知某人使用RSA加解密软件发送了一个明文,其中所有加密数据已被截获,在这些加密的数据中存在着各种参数选择不当的问题,导致加密的安全性受到影响。我们需要从加密数据恢复该明文及RSA体制的参数,并给出明文和参数。
解题过程
首先需要解析密文的结构,帧数据的数据格式如下,其中数据都是 16 进制表示,结构如下 1024bit模数N | 1024bit加密指数e | 1024bit密文me mod N。解析了帧的结构后可以对密文进行分析。
在本题中主要用到的攻击方式有下面几种
1,共模攻击
共模攻击即用两个及以上的公钥(n,e)来加密同一条信息m。已知有密文:c1 = pow(m, e1, n),c2 = pow(m, e2, n),当e1,e2互质,则有gcd(e1,e2)=1,根据扩展欧几里德算法,对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整数x,y使得gcd(a,b)=ax+by。所以得到:e1s1+e2s2=1。因为e1和e2为正整数,所以s1、s2皆为整数,但是一正一负,此时假设s1为正数,s2为负数。
def common_modulus_attack(ns,es,cs):
# 寻找公共模数
index1 = 0
index2 = 0
for i in range(21):
for j in range(i+1, 21):
if ns[i] == ns[j]:
#print('Same modulus found! ({}, {})'.format(i, j))
index1, index2 = i, j
e1 = int(es[index1], 16)
e2 = int(es[index2], 16)
n = int(ns[index1], 16)
c1 = int(cs[index1], 16)
c2 = int(cs[index2], 16)
g, s1, s2 = extended_gcd(e1, e2)
# 求模反元素
if isinstance(s1, int) and s1 < 0:
s1 = -s1
c1 = gmpy2.invert(c1, n)
elif isinstance(s2, int) and s2 < 0:
s2 = -s2
c2 = gmpy2.invert(c2, n)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
#print('Recovered message: {}'.format(hex(m)))
result = binascii.a2b_hex(hex(m)[2:])
return result
2,因数碰撞法
因数碰撞法是指,如果p或者q在多个RSA加密中出现多次,那么生成不同的n可能会有相同的因子,假设p相同,q不同,那么有 n1=pq1,n2=pq2。易得 gcd(n1,n2)=p。这样就可以将其各自的私钥解出来了。
def factor_collision_attack(ns,es,cs):
p, index1, index2 = None, None, None
for i in range(21):
for j in range(i+1, 21):
if int(ns[i], 16) == int(ns[j], 16):
continue
gcd = gmpy2.gcd(int(ns[i], 16), int(ns[j], 16))
if gcd != 1:
p, index1, index2 = gcd, i, j
break
if p:
break
q1 = int(ns[index1], 16) // p
q2 = int(ns[index2], 16) // p
phi1 = (p-1)*(q1-1)
phi2 = (p-1)*(q2-1)
d1 = gmpy2.invert(int(es[index1], 16), phi1)
d2 = gmpy2.invert(int(es[index2], 16), phi2)
plaintext1 = gmpy2.powmod(int(cs[index1], 16), d1, int(ns[index1], 16))
plaintext2 = gmpy2.powmod(int(cs[index2], 16), d2, int(ns[index2], 16))
return [binascii.a2b_hex(hex(plaintext1)[2:]), binascii.a2b_hex(hex(plaintext2)[2:])]
3,使用费马分解法分解n
费马分解法是一种基于数论的因数分解算法,可以用于破解RSA加密算法。其基本思路是通过找到一个接近平方根的数,来使得模数n和这个数的平方的差刚好是一个完全平方数。接着,通过对这个完全平方数开方,得到两个因子,从而分解出n的因子。
#使用费马分解法分解n
def fermat_factorization(n):
B = math.factorial(2**14)
u = 0
v = 0
i = 0
u0 = gmpy2.iroot(n, 2)[0] + 1
while i <= (B - 1):
u = (u0 + i)**2 - n
if gmpy2.is_square(u):
v = gmpy2.isqrt(u)
break
i = i + 1
p = u0 + i + v
return p
#输出费马分解法攻击的结果
def get_content_of_frame10(ns):
# 分解Frame 10
n = int(ns[10], 16)
p = fermat_factorization(n)
# 已知p,直接计算q和phi(n)
c = int(cs[10], 16)
e = int(es[10], 16)
q = n // p
phi_n = (p - 1) * (q - 1)
# 计算私钥d并解密
d = gmpy2.invert(e, phi_n)
m = gmpy2.powmod(c, d, n)
final_plain = binascii.a2b_hex(hex(m)[2:])
return final_plain
4,Pollard p-1分解法攻击
Pollard p-1分解法的核心思想是利用数学中的一些性质,通过选择合适的参数来使得因子较小的数的概率增加,从而更容易进行分解。
算法的基本原理是,假设我们要分解的RSA加密算法中使用的质数p是在一个相对较小的范围内,我们可以选择一个合适的整数a,使得a的幂次数连续乘起来与p取模的结果包含了一些小的质因数。具体来说,假设a的幂次数连续乘起来为b = a^(k1) * a^(k2) * … * a^(kr),其中ki表示每个幂次数,r表示幂次数的个数,那么我们可以通过计算gcd(b-1,p) 来得到p的一个因子。
Pollard p-1分解法并不能保证一定能够分解出 RSA 加密算法中使用的质数,因为它依赖于a的选择和幂次数的取值,如果选择的参数不合适或者幂次数取值不合理,那么可能无法得到有效的结果。
#Pollard p-1分解法攻击
def pollard_p_minus_1_factorization(n):
b = 2**20
a = 2
for i in range(2, b+1):
a = pow(a, i, n)
d = gmpy2.gcd(a - 1, n)
if 1 < d < n:
return d
return None
def pollard_resolve(ns,es,cs):
index_list = [2, 6, 19]
plaintexts = []
for i in index_list:
n = int(ns[i], 16)
c = int(cs[i], 16)
e = int(es[i], 16)
p = pollard_p_minus_1_factorization(n)
#print("p of " + str(i) + " is : " + str(p))
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = gmpy2.powmod(c, d, n)
plaintexts.append(binascii.a2b_hex(hex(m)[2:]))
return plaintexts
5,低加密指数攻击
5,低加密指数攻击
RSA低加密指数广播攻击是一种攻击RSA加密算法中使用了相同低加密指数的多个密文的情况。攻击者可以通过这些密文来推断出RSA加密中的私钥,从而解密所有其他密文。
攻击的原理基于RSA加密算法中的加密过程,即c=m^e mod n,其中c是密文,m是明文,e是加密指数,n是两个大质数p和q的乘积。在RSA加密中,选择e作为加密指数是非常重要的,通常情况下会选择e=3或e=65537等较小的质数,以便于快速计算密文。
攻击者利用相同的低加密指数e加密多个明文m1,m2,…,mk,得到对应的密文c1,c2,…,ck假设攻击者已经知道e和n并且可以获得每个密文的ci和n的值。然后,攻击者可以使用中国剩余定理来求解me,因为对于i=1,2,…,k,me ≡ ci (mod n),因此 m^e 可以被表示为m^e≡xi(mod p) 和 m^e ≡yi(mod q),其中xi≡ci(mod p),yi≡ci(mod q)。然后,攻击者可以通过求解离散对数问题来计算m,具体来说,可以使用欧拉定理m^(φ(n))≡1(mod n)来计算d=e^(-1)(mod φ(n)),其中φ(n)=(p-1)(q-1) 是欧拉函数,e(-1)表示e在模φ(n)意义下的逆元。最后,攻击者可以通过计算cd(mod n) 来解密所有的密文,因为cd=(me)d=m(ed)≡m(mod n)。
def chinese_remainder_theorem(items): #中国剩余定理
N = 1
for a, n in items:
N *= n
result = 0
for a, n in items:
m = N//n
d, r, s = extended_gcd(n, m)
if d != 1:
N = N//n
continue
result += a*s*m
return result % N, N
# 低加密指数攻击
def bruce_e_3(ns,cs):
bruce_range = [7, 11, 15]
for i in range(3):
c = int(cs[bruce_range[i]], 16)
n = int(ns[bruce_range[i]], 16)
print("This is frame" + str(i))
for j in range(20):
plain = gmpy2.iroot(gmpy2.mpz(c+j*n), 3)
print("This is test" + str(j))
print(binascii.a2b_hex(hex(plain[0])[2:]))
def low_e_3(ns,cs):
sessions=[{"c": int(cs[7], 16) ,"n": int(ns[7], 16)},
{"c":int(cs[11], 16) ,"n":int(ns[11], 16)},
{"c":int(cs[15], 16) ,"n":int(ns[15], 16)}]
data = []
for session in sessions:
data = data+[(session['c'], session['n'])]
x, y = chinese_remainder_theorem(data)
# 直接开三次方根
plaintext7_11_15 = gmpy2.iroot(gmpy2.mpz(x), 3)
return binascii.a2b_hex(hex(plaintext7_11_15[0])[2:])
def low_e_5(ns,cs):
sessions=[{"c": int(cs[3], 16),"n": int(ns[3], 16)},
{"c":int(cs[8], 16) ,"n":int(ns[8], 16) },
{"c":int(cs[12], 16),"n":int(ns[12], 16)},
{"c":int(cs[16], 16),"n":int(ns[16], 16)},
{"c":int(cs[20], 16),"n":int(ns[20], 16)}]
data = []
for session in sessions:
data = data+[(session['c'], session['n'])]
x, y = chinese_remainder_theorem(data)
# 直接开五次方根
plaintext3_8_12_16_20 = gmpy2.iroot(gmpy2.mpz(x),5)
return binascii.a2b_hex(hex(plaintext3_8_12_16_20[0])[2:])
6,结果分析
经过上述算法后,我们可以通过调用函数来获得部分密文的攻击结果,代码和结果如下
if __name__ == '__main__':
ns=[] #1024bit模数N
es=[] #1024bit加密指数e
cs=[] #1024bit密文m^e mod N
for i in range(21):
with open("D:/学习/大作业/现代密码学/RSA加密体制破译/密码挑战赛赛题三/附件3-2(发布截获数据)/Frame"+str(i), "r") as f:
temp = f.read()
ns.append(temp[0:256])
es.append(temp[256:512])
cs.append(temp[512:768])
result_0=common_modulus_attack(ns,es,cs)
#print(result_0) # Frame0 与 Frame4 的解密结果是: My secre
result_1=factor_collision_attack(ns,es,cs)
#print(result_1[0]) # Frame1解密结果是: . Imagin
#print(result_1[1]) # Frame18解密结果是: m A to B
result_2=get_content_of_frame10(ns)
#print(result_2) # Frame10解密结果是: will get
result_3=pollard_resolve(ns,es,cs)
#print(result_3[0]) # Frame2 解密结果是: That is
#print(result_3[1]) # Frame6 解密结果是: "Logic "
#print(result_3[2]) # Frame19 解密结果是: instein.
result_4=low_e_5(ns,cs)
#print(result_4) ## Frame3 Frame8 Frame12 Frame16 Frame20 解密结果是: t is a f
总结
RSA加密算法是一种常用的公钥加密算法,但它并不是绝对安全的,存在多种攻击方法可以破解其加密过程。在本次实验中先使用了五种常见的RSA攻击算法,分别是共模攻击、因数碰撞、低加密指数广播攻击、Pollard p-1分解法攻击和费马分解法攻击。最后使用猜测攻击来尝试还原明文。通过本次实验加深了我队RSA加密算法的理解,明白了RSA加密在使用时应注意的事项。
参考文献
Boneh D. Twenty years of attacks on the RSA cryptosystem[J]. Notices of the AMS, 1999, 46(2): 203-213.
https://github.com/blank-vax/RSA_breaking
https://blog.csdn.net/pigeon_1/article/details/114371456