简单RSA攻击方式
RSA攻击方式总结
1.模数分解
1).解题思路
a).找到RSA算法中的公钥(e,n)
b).通过n来找到对应的p和q,然后求得φ(n)
c).通过gmpy2.invert或者gmpy2.gcdext可以求得e的逆元d
d).通过pow函数将密文解密(pow(a,b,c)函数的速度比直接写a**b mod c 要快)
2).直接分解n
a).通常情况下,如果n的长度小于256bit(二进制),那么可以通过本地的工具进行分解(RSATool2v17)
b).如果n的长度较大,可以尝试在相关网站上进行查询。这些网站上存储着已经分解过的n对应的p和q
c).通常,如果n的长度大于512bit,可以考虑下面的方法进行解题
3).利用公约数(模不互素攻击)
a).如果题目中两次公钥加密给出了n1和n2有相同的素因子,那么可以通过gmpy2.gcd求出素因子p,然后在分别对n1和n2求出q1和q2
b).这种需要利用公约数的题目,通常情况下给出的n长度都在2048bit,4096bit,无法强行破解
4).一些难度比较大的分解方法
a).针对大整数的分解有很多种算法,性能上各有优异,有指数级分解方法:Fermat平方差方法,Pollard 方法(ρ,ρ-1)(Pollard's rho algorithm),二次型分解算法,椭圆曲线分解法(ECM)和亚指数级分解方法:连分数分解算法,筛法(二次筛选法(QS),一般数域筛法(GNFS))等等。
b).但是,有一个开源项目yafu可以自动化的完成分解,不论n的大小(密码学工具使用)
5).做题过程中get到的方法,有可能是方法4里面的包括的
-
知道了\(\phi (n)\),就可以开方:
这个算法涉及两个点,开方后的取值的大小 Q, Q<p<q, 或 p<Q<q
1.p<Q<q
假设 p=7,q=11,,则(p-1)*(q-1)=60,
iroot(60,2)[0] = 7 (iroot()表示开根号,[0]表示取整数部分)
所以用sympy.next(7),就可以求得7后面的一个素数112.Q<p<q
假设 p=3,q=5, 则(p-1)*(q-1)=8,
iroot(8,2)[0] = 2 (iroot()表示开根号,[0]表示取整数部分)
所以用sympy.next(2),就可以求得2后面的一个素数33.
虽然Q取值范围不定,但是我们可以肯定的是,Q一定小于max(p,q),即p,q中最大的那个数。所以通过nextprime(Q),就可以求得p,q中任意的值。再用已知的 (ed-1)//k == (p-1)(q-1),就可以求得(p-1)或是(q-1)的值了。最后再进行素性检测就可以了
原文地址:BUUCTF RSA题目全解2的第十题
2.低加密指数攻击
RSA中,e也被称作加密指数,由于e是从(1,φ(n))中任意选取的,有些时候为了缩短加密时间,可以适当的选择较小的e。
1).e=3的小明文攻击(暴力开方破解)
a).当e=3时,如果明文过小,导致明文的三次方仍然小于n,那么直接对密文三次开方,就可以得到明文(a如果小于n,则a mod n==a)
b).如果明文的三次方大于n,但是不是很大,就可以设k,有:
对k进行爆破,若 \(c-kn\) 可以开三次根式,那么可以直接得到明文
c).使用加密指数e=\(2^{16} +1\)=65537(费马素数[1])可以避免一些对e=3的攻击
d).也可以将明文进行填充
2).低加密指数广播攻击(Johan Håstad广播攻击)
a).如果选取的加密指数e较小,且给多个接收方用这个e发送相同的明文(n不同),那么就可以进行广播攻击
b).当收集到的加密消息个数大于或等于e时,有(假设e=3)如果是根据中国剩余定理,那么加密消息至少得有三个(一个和两个不能得出一组确切的解)
则根据中国剩余定理,可知:
对m开e次方根,即可求得明文。
注:\(N_i^{-1}\)是\(N_i\)模\(n_i\)的逆元
3)Coppersmith短填充攻击
3.低解密指数攻击
a).同样的,低解密指数通常会加快解密的速度,但是也会带来安全隐患
b).如果解密指数\(d<\frac{1}{3}N^\frac{1}{4}\),那么一种由Wiener提出的基于连分式的算法可以解出该解密指数(使用了连分式的经典数论理论以及如何找到用有理数对二次无理数最合理的逼近方法)。工具rsa_wiener_attack
c).再后来,Boneh指出,使用小于\(\sqrt{N}\)的解密指数的系统可能容易受到此类攻击
由图可知,解密指数的攻击范围被提高了
4.共模攻击
如果出现了用相同的模数n来加密相同的明文m(加密指数e不一样),则会得到
此时
a).可以利用米勒公式,通过一个人的秘钥(\(e_i,d_i\))分解n得到p,q,从而可以通过别人的公钥来计算别人的私钥
b).如果两个加密指数互素,那么根据扩展你欧几里得方程可知,有:
那么可得
所以,可以得到
所以,我们在完全不知道密钥的前提下,得到了明文m
注意:在Python的pow函数中,指数不可以为负数,而\(s_1\),\(s_2\)中必然有一个是负数,则需要将对应的c取反,再用-s作为指数进行运算[2]
k,s1,s2=gmpy2.gcdext(e1,e2)
if s1<0:
s1=-s1
c1=gmpy2.invert(c1,n)
else:
s2=-s2
c2=gmpy2.invert(c2,n)
m=pow(c1,s1,n)*pow(c2,s2,n)%n #虽然不知道为什么,但是好像这样写运算会很快
print("所求明文为:",m)
print(long_to_bytes(m))
5.前向搜索攻击[3]
如果知道所有可能消息的集合,而且该集合相对来说比较小,则攻击者只需要将所有的消息进行加密直到找到一个匹配者。例 如,如果已知消息为‘YES’或‘NO’,那么只需要计算一次就知道哪一个是明文。因此,特别是在比较短小的消息的情况下,应该通过在前面或者后面添加随机位数的填充信息。
6.教科书RSA的两个性质和攻击方法[4]
教科书RSA是确定性的,意即同样的明文m总是生成同样的密文c。这就使密码本攻击成为可能:攻击者预先计算出全部或部分的\(m\to c\) 对照表保存,然后搜索截获的密文匹配即可。确定性也意味着教科书RSA不是语义安全的,密文会泄露明文的某些信息。密文重复出现,表明发送方在重复发送相同的消息。
教科书RSA具有延展性 (malleable),对密文进行特定形式的代数运算,结果会反映到解密的明文中。比如有两段明文 \(m_1\)和\(m_2\),加密后产生\(c_1= m_1^{e}\%N\)和\(c_2= m_2^e\%N\),那么(\(c_1*c_2\))解密会得到什么?看如下等式:
\[(c_1*c_2)^d\equiv m_1^{ed}*m_2^{ed}\equiv m_1*m_2(mod\ N) \]所以两段密文的乘积解密后得到的明文,等于两段明文的乘积。这一特性对一般的RSA加密系统是有害的,它为选择密文攻击 (chosen-ciphertext attack) 提供了机会。下面举例两种攻击场景:
1.设想有一个RSA解密机可以用内部保存的私钥(N,d)解密消息。基于安全考虑,解密机会拒绝同样的密文重复输入。攻击者马文发现一段密文,直接输入到解密机被拒绝,因为密文c以前被解密过。马文找到一种办法破解。他自己准备一段明文,用公钥(N,e)加密生成新的密文\(c'=r^ec\%N\),然后将密文\(c'\)输入到解密机。解密机没有解过这一段新的密文,所以不会拒绝。解密的结果是
\[m'\equiv (c')^d\equiv r^{ed}c^d\equiv rm(mod\ N) \]现在马文有了\(m'\),他用公式\(m\equiv m'r^{-1}(mod\ N)\)就可以计算出c对应的m
2.假设马文想让鲍勃在一段消息\(m\)上签名,但是鲍勃在看过消息内容后拒绝了。马文可以使用称为盲签名的攻击手段可以达成他的目标。他挑选一段随机的消息\(r\),生成 \(m'=r^em\%N\),然后把\(m'\)拿给鲍勃签名。鲍勃可能觉得\(m'\)无关紧要,就签了。鲍勃签名的结果是\(s'=(m')^d\%N\)。现在马文用公式\(s=s'r^{-1}\%N\)就拿到了鲍勃对原来消息的签名。为什么?原因是
\[s^e\equiv (s')^er^{-e}\equiv(m')^{ed}r^{-e}\equiv m(mod\ N) \]
7.其它常见的题型总结
1).给出了dp、dq的题
首先明确dp、dq是什么:
情况1:dp、dq都给出来了
解决方法:
证明:已知\(m\equiv c^{d}(mod\ n)\),可得到\(m=c^{d}+k*n\)
又因为\(n=p*q\),可得\(m=c^{d}+p*q*k\),对它分别取余p,q
可得\(m_1\equiv c^{d}(mod\ p)\)(1),\(m_2\equiv c^{d}(mod\ q)\)(2)
式(1)可以变形为\(c^{d}=kp+m_1\),带入(2),可得\(m_2\equiv (kp+m_1)(mod\ q)\)
两边同时减去\(m_1\),可得\((m_2-m_1)\equiv kp(mod\ q)\)
因为p,q互素(\(gcd(p,q)=1\)),可以求得p的逆元,得到
将它变形为
然后与上面(1)的变形合并,可得
将\(m\equiv c^{d}(mod\ n)\),得到
现在根据费马小定理[5]可以将dp、dq带入\(m_1\)、\(m_2\)中得到
这一步带入的证明见注释[6]。
则式子\((\alpha)(\beta)(\gamma)\)即为所求。
from Crypto.Util.number import long_to_bytes
c=int(input("请输入密文c"))
n=int(input("请输入n(不存在请输入0)"))
#存在给出p,q没有给出n的情况
p=int(input("请输入p(不存在请输入0)"))
q=int(input("请输入q(不存在请输入0)"))
dp=int(input("请输入dp"))
dq=int(input("请输入dq"))
if n==0:
n=p*q
m1=pow(c,dq,q)
m2=pow(c,dp,p)
k=(m2-m1)*(p-1)%q
m=k*p+m1%n
print("所求明文为:",m)
print(long_to_bytes(m))
情况2:只给出了dp/dq的题
假设给出的是dp,则有\(dp=d\%(p-1)\),变形可得
变形后与式子\(d*e\equiv 1(mod\ \phi(n))\)结合,可得
设:X=\(k_2*(q-1)-k_1*e\),有\(dp*e=X*(p-1)+1\),因为\(dp=d\%(p-1)\),所以\(dp<p-1\),可得\(X<e\),\(X\in(0,e)\)和式子\((p-1)=\frac{dp*e-1}{X}\)
则有遍历区间(0,e),找到一个X可以整除(不是被整除)(dp*e-1),且对应的p能够整除n的情况,然后求出q,即可得到明文。
给出dq同理。
for X in range(1,e):
if (dp*e-1)%X == 0:
if n%(((dp*e-1)/X)+1)==0:
p=((dp*e-1)/X)+1
q=n/(((dp*e-1)/X)+1)
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)%phi
import gmpy2
e = 65537
n = 248254007851526241177721526698901802985832766176221609612258877371620580060433101538328030305219918697643619814200930679612109885533801335348445023751670478437073055544724280684733298051599167660303645183146161497485358633681492129668802402065797789905550489547645118787266601929429724133167768465309665906113
dp = 905074498052346904643025132879518330691925174573054004621877253318682675055421970943552016695528560364834446303196939207056642927148093290374440210503657
c = 140423670976252696807533673586209400575664282100684119784203527124521188996403826597436883766041879067494280957410201958935737360380801845453829293997433414188838725751796261702622028587211560353362847191060306578510511380965162133472698713063592621028959167072781482562673683090590521214218071160287665180751
for X in range(2,e):
if (dp*e-1)%X == 0:
if n%(((dp*e-1)//X)+1)==0: #这里和下面的//如果换成/就会出现OverflowError: int too large to convert to float的问题
p=((dp*e-1)//X)+1
q=n//(((dp*e-1)//X)+1)
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)%phi
m=pow(c,d,n)
参考来源:
-
《密码学导引》(美)Paul Garrett著
-
《现代密码分析学——破译高级密码的技术》(美)Christopher Swenson著
费马素数:是一类特殊形式的数,形如\(2^m+1\)的数如果是素数,那么它被称作费马素数。与之相对的是梅森素数:形如\(2^n-1\)的素数 ↩︎
\(c_1^{s}=(c_1^{-1})^{-s}\%n\) ↩︎
这一段来自参考来源2 ↩︎
这一段来自参考来源5 ↩︎
费马小定理:如果有条件:1). p为素数,2). \(gcd(a,p)=1\),则有式子\(a^{p-1}\equiv1(mod\ p)\)成立。这个定理也常常用来计算乘法逆元:\(a*a^{p-2}\equiv1(mod\ p)\),则\(a^{p-2}\)是a的乘法逆元 ↩︎
证明:由于费马小定理,可知在c和p互素的情况下(p必然是素数),那么将式子\(d=dp+k*(p-1)\)带入式子\(m_2\equiv c^d (mod\ p)\),可得 \(m_2\equiv c^{dp+k(p-1)}(mod\ p)\),\(m_2\equiv c^{dp}*c^{k(p-1)}(mod\ p)\)。因为\(c^{(p-1)}\equiv 1(mod\ p)\),可得:\(m_2\equiv c^{dp}*(1)^k (mod\ p)\),\(m_2\equiv c^{dp}(mod\ p)\),根据对称性,可知\(m_1\)同理。 ↩︎