RSA礼包
RSA简介
RSA算法涉及三个参数,n,e,d,私钥为 d,公钥对为 n,e
其中N = pq (p,q均为大素数)
ed = 1 mod (p-1)(q-1)
(p-1)(q-1)的值为n的欧拉函数
c 为密文,m 为明文,则加密过程如下:
c ≡ me mod n
解密过程如下:
m ≡ cd mod n
n,e 是公开的情况下,想要知道 d 的值,必须要将 n 分解计算出 n 的欧拉函数值,而 n 是两个大素数 p,q 的积, 将其分解是困难的。
但是对于特定的情况可以使用特殊方法进行分解
数据收集
n = []
e = []
c = []
m = {}
solved = []
name = ['./Frame' + str(i) for i in range(21)]
for i in range(21):
f = open(name[i], 'r')
data = f.read()
tn, te, tc = int(data[:256], 16), int(data[256:512], 16), int(data[512:], 16)
n.append(tn)
e.append(te)
c.append(tc)
题目解法
直接分解n
素数分解问题是困难的,但是可以通过计算机进行暴力分解。
通常意义上来说,一般认为 2048bit 以上的 n 是安全的。现在一般的公钥证书都是 4096bit 的证书
如果 n 比较小,那么可以通过工具进行直接 n 分解,从而得到私钥。如果 n 的大小小于 256bit,那么我们通过本地工具即可爆破成功。
例如采用 windows 平台的 RSATool 2v17,可以在几分钟内完成 256bit 的 n 的分解。
大数分解算法(Pollard (6,7,5))
def Pollard_p_1(N):
a = 2
f = a
# precompute
while 1:
for n in range(1, 200000):
f = powmod(f, n, N)
if is_prime(n):
d = gcd(f - 1, N)
if 1 < d < N:
return d, N // d
elif d >= N:
f = next_prime(a)
break
else:
break
def _GetPlain(c):
tmp = hex(c)[2:]
if tmp[:16] != '9876543210abcdef':
return 0
number = int(tmp[16:24], 16)
plain = long_to_bytes(int(tmp[-16:], 16))
m[number] = plain
return 1
def GetPlain(p, q, e, c):
phi = (p - 1) * (q - 1)
d = invert(e, phi)
m = powmod(c, d, p * q)
return _GetPlain(m)
def detect6_7_5():
for i in range(21):
if i not in solved:
tmp = Pollard_p_1(n[i])
if isinstance(tmp, tuple):
p, q = tmp
if GetPlain(p, q, e[i], c[i]):
solved.append(i)
低加密指数攻击(3,8,12,16,20)
在 RSA 中 e 也称为加密指数。由于 e 是可以随意选取的,选取小一点的 e 可以缩短加密时间(比如 3),但是选取不当的话,就会造成安全问题
当 e=3 时,如果明文过小,导致明文的三次方仍然小于 n,那么通过直接对密文三次开方,即可得到明文。
def CRT(mi, ai):
M = reduce(lambda x, y: x * y, mi)
ai_ti_Mi = [a * (M // m) * invert(M // m, m) for (m, a) in zip(mi, ai)]
return reduce(lambda x, y: x + y, ai_ti_Mi) % M
def small_e_boardcast_attack(nlist, e, clist):
m = CRT(nlist, clist)
tmp = iroot(m, e)
if tmp[1] == 1:
return tmp[0]
else:
return 0
def _GetPlain(c):
tmp = hex(c)[2:]
if tmp[:16] != '9876543210abcdef':
return 0
number = int(tmp[16:24], 16)
plain = long_to_bytes(int(tmp[-16:], 16))
m[number] = plain
return 1
def detect3_8_12_16_20():
e = 5
num = [3, 8, 12, 16, 20]
nlist = [n[i] for i in num]
clist = [c[i] for i in num]
m = small_e_boardcast_attack(nlist, e, clist)
if _GetPlain(m):
for i in num:
solved.append(i)
共模攻击(0,4)
如果在 RSA 的使用中使用了相同的模 n 对相同的明文 m 进行了加密,那么就可以在不分解 n 的情况下还原出明文 m 的值。
帧0,4使用了相同的模n
#欧几里得算法
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def _GetPlain(c):
tmp = hex(c)[2:]
if tmp[:16] != '9876543210abcdef':
return 0
number = int(tmp[16:24], 16)
plain = long_to_bytes(int(tmp[-16:], 16))
m[number] = plain
return 1
#共模攻击(0,4)
def same_module_attack(N, e1, e2, c1, c2):
d1 = invert(e1, e2)
d2 = (d1 * e1 - 1) // e2
true_c2 = invert(c2, N)
return (powmod(c1, d1, N) * powmod(true_c2, d2, N)) % N
def detect0_4():
for i in range(21):
for j in range(21):
if i != j and n[i] == n[j] and e[i] != e[j]:
_GetPlain(same_module_attack(n[i], e[i], e[j], c[i], c[j]))
参考链接
https://blog.csdn.net/weixin_44145452/article/details/109924843
https://www.cnblogs.com/jcchan/p/8426731.html
https://www.tr0y.wang/2017/10/31/RSA2016/#%E8%A7%A3%E9%A2%98%E8%BF%87%E7%A8%8B
https://blog.csdn.net/weixin_45859850/article/details/109785669