Rsa基础知识
1
| rsa是经常出现在ctf考题中的一类题,它是基于大素因数分解难题产生的公钥密码
|
数学基础
素数
素数又叫质数,除了1和其本身,不能被其他数整除的数叫做素数
模运算
模运算是取余运算,其符号为mod
同余
两个数a,b 对同一个数m取余结果相同,则称a与b模m同余
互质
当两个正整数除了1之外没有其他公因子,我们就称这两个数互质
任意两个质数一定互质
欧拉函数
数论中,对正整数n,欧拉函数φ(n)指的是小于或等于n的正整数中与n互质的数
易知质数n的欧拉函数是n−1
若不是质数,其欧拉函数有公式:
欧拉定理
若两个正整数a, n互质,则如下等式成立
aφ(n)≡1modn
费马小定理
是欧拉定理的特例,当n是质数并且a和n互质
ap−1≡1modp
模逆元
对整数a,b,若 ab≡1modn 则称 a 和 b 关于模 n 互为模倒数,也称为模反元素或模逆元,还可以记作
b=a−1modn
Rsa原理
- 准备两个非常大的素数p,q
- 计算n=p∗q
- 计算φ(n),由于p, q都是素数,所以φ( n)=φ( p)∗φ( q),根据数论基础知识,φ( p)= p − 1,φ( q)= q − 1
- 选取公钥e与φ(n)互素φ( p)
- 求得私钥d,d∗e≡1modφ(n),也就是d是e模φ(n)的逆元
加密公式
用公钥对明文m进行加密,得到密文c
c=E(m)=memodn
解密公式
用私钥进行解密
m=D(c)=cdmodn
RSA证明
由于d∗e≡1modφ(n),所以d∗e=k∗φ(n)+1
已知c≡memodn,则
cdmodn≡me∗dmodn≡mk∗φ(n)+1modn
此时要分情况讨论
-
当 m 和n互质的时候
有 mφ(n)=1modn则此时有
mk∗φ(n)mk∗φ(n)+1≡1kmodn≡1modn≡mmodn
也就是说cdmodn≡m
-
当 m 和n不互质的时候
由于m<n,n=pq,所以m是p或者q的倍数,意为m=t∗q,这里的t为正整数
也就是说gcd(m,p)=1
由费马小定理,mφ(p)=1modp,两边同时进行φ(q)次方得到mφ(n)≡1modp,
那么存在正整数n,使得mφ(n)=1+n∗p
两边同时乘以m=t∗q,得到mφ(n)+1=m+nt∗p∗q,也即是
mφ(n)+1≡mmodn
Rsa常见攻击方式
关于模数的攻击
模不互素
攻击原理
当两个模数n不互素的时候,其公因子就是p或q,因此可以直接将n分解
共模攻击
生成秘钥的过程中使用了相同的模数n,此时用不同的秘钥e加密同一信息m:
c1=me1modn
c2=me2modn
若是两次生成的公钥 e1 和 e2 互素,则根据扩展欧几里得算法,存在 s 和 t 使得:
s∗e1+t∗e2=gcd(e1,e2)=1
所以
c1sc2t≡mse1mte2modn≡mse1+te2modn≡mmodn
因此只要计算c1sc2t即可得出明文c
此处贴上通解代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import primefac
def same_n_attack(n,e1,e2,c1,c2): def egcd(a,b): x, lastX = 0, 1 y, lastY = 1, 0 while (b != 0): q = a // b a, b = b, a % b x, lastX = lastX - q * x, x y, lastY = lastY - q * y, y return (lastX, lastY) s = egcd(e1,e2) s1 = s[0] s2 = s[1] if s1 < 0: s1 = -s1 c1 = primefac.modinv(c1, n) if c1 < 0: c1 += n elif s2 < 0: s2 = -s2 c2 = primefac.modinv(c2, n) if c2 < 0: c2 += n m = (pow(c1, s1, n) * pow(c2, s2, n)) % n return m
|
dp泄露
首先dp≡dmod(p−1),这个dp是比较重要的,因为其中蕴含了p的信息,只要稍加推导就能得到p和q。
此时推导如下:
dp∗edp∗ed∗e≡d∗emod(p−1)=d∗e+k1∗(p−1)=dp∗e+k1(p−1)
由于d∗e≡1modφ(n),所以
dp∗e+k1(p−1)dp∗e+k1(p−1)dp∗e+k1(p−1)dp∗edp∗e令(k2(q−1)−k1)dp∗e≡1modφ(n)≡1mod(p−1)(q−1)=k2∗(p−1)(q−1)+1=k2(p−1)(q−1)+1−k1(p−1)=(k2(q−1)−k1)(p−1)+1为x,则该式变成:=x(p−1)+1
此时由于dp<(p−1),因此e>x,所以x就在(1,e),此时只要遍历(1,e),并且计算
(dp∗e−1)/x
只要最后结果是整数,那么就大概率是(p−1),这时候继续往下算,如果解密结果不是乱码就基本上是正确的了。脚本如下:
1 2 3 4 5 6 7
| for i in range(1,e): if (dp*e-1)%i == 0: if n%(((dp*e-1)/i)+1)==0: p=((dp*e-1)/i)+1 q=n/(((dp*e-1)/i)+1) phi = (p-1)*(q-1) d = gmpy2.invert(e,phi)%phi
|
e特别大的情况
e特别大的时候遍历都需要花很长的时间,再进行判断以及计算更需要大量时间,可能跑一年都跑不出来。这时候我们换一种分析方式:
dp∗edp∗e2dp∗e−1k∗pp≡d∗emod(p−1)≡1mod(p−1)≡1modp=2dp∗e−1−1=gcd(2dp∗e−1−1,n)
p和q选取的特别近