RSA总结:基本攻击手段与实际应用手法整理

前言:本文主要讲述RSA的加解密原理与基本过程,整理常见的RSA破解手段,以及如何将原理应用到CTF等相关赛事或实际工作中。通过阅读本文可以使初学者快速入门RSA,本文中的案例将提供注释详细的解密脚本,如有问题欢迎讨论交流!

0X01:RSA简介

关于RSA的介绍各种百科上都有不少,这里只做简单介绍。

RSA是一种非对称加密算法,是第一种能同时应用于数字签名和加密的算法。RSA的安全性依赖于对大整数做因式分解的困难性,从算量角度来说,一般认为对2048bit以上的整数做因式分解是难以实现的。而由于如今使用的公钥基本全部采用4096bit,因此RSA在今天仍是一种十分可靠的加密算法,在实际生产中得到了广泛应用。

0X02:数学知识引入

这里介绍基本的数学概念,其他算法将在介绍攻击手法时给出解释

一,互质数

公因数只有1的一组非零自然数称为互质数,例如 17,31  公因数只有1,为互质数

二,模逆元

任意三个整数a,b,N,如果满足 a*b mod N=1,则称b是a关于N的模逆元

如 3*7 mod 20 =1 则7为3对于20的模逆元,同样3也为7对于20的模逆元

三,欧拉函数

欧拉函数求的是小于某个数 n 并与 n 互质的数的个数

φ(n) = (p-1)(q-1)

0X03:RSA加解密过程

公钥与私钥生成

第一步:随机选择两个不相等的质数p和q。

例如 p=35,q=17

第二步:计算p和q的乘积n。

n = 35*17 = 595

第三步:计算n的欧拉函数φ(n)。

φ(n) = (p-1)(q-1) =34*16= 544

第四步:选择一个满足1< e < φ(n)的整数e,且e与φ(n) 互质。

例如:此处选择e=13

第五步:计算e对于φ(n)的模反元素d。

 e=13, φ(n)=544,13x + 544y = 1 算出一组整数解为 (x,y)=(293,-7),即 d=293。

第六步:(n,e)封装为公钥用于加密,(n,d)封装成私钥用于解密。

所以本例中 公钥为(595,13)  私钥为(595,293)

加密与解密公式

密文 c=m^e mod n

明文 m=c^d mod n

0X04:RSA在CTF赛事中的常见攻击手段的判断与使用

  • 提取n,e,c 的方法

1.对于题目直接给出文本文件的情况,一般直接打开就能获得公钥和密文信息,稍微绕点弯子的可能会将原数据进行编码(一般为base64或hex),解码后提取信息即可。

2.对于另一种给出 .pem文件(存储公钥信息)和 .enc文件(存储密文信息)的情况,可以使用 命令行工具openssl提取出公钥信息

常用命令:openssl rsa -pubin -text -modulus -in pubkey.pem

或者写脚本读出数据(个人比较推荐,因为方便对数据做后续处理),python3代码描述如下

from Crypto.PublicKey import RSA

r=open('CTFquestion/RSA3/pubkey.pem').read()
pub=RSA.importKey(r)
#读取pem文件,获取n,e
n=pub.n
e=pub.e
#读取enc文件,获取c
flag_encode= open('CTFquestion/RSA3/flag.enc','rb').read()
c=int(flag_encode.hex(),16)
print("n:",n)
print("e:",e)
print("c:",c)

3.另外听说还有一种类型是给你一个流量包文件(pacp),需要拖进wireshark分析一下交互过程,由于本人在练习中还没有遇到过此类问题,因此不做过多赘述,感兴趣的朋友可以自行查阅资料。

  • 简单情况,直接分解N

适用情况:公钥中给定的 n 较小,可以直接对 n 分解因式推出 p q。

ps:对于分解因式推荐使用 yafu ,这是目前较为通用的一款因式分解工具,它会自动尝试连分数法,二次筛选法等各种数学分解方式,对于小于 512bit 的整数分解成功率极高

使用方法:命令行启动yafu后,输入factor(需要分解的整数)即可。

对于n过长的情况,新建一个文件p.txt,内容里写上n的值,最后面一定要换行。
然后运行命令为   yafu-x64 "factor(@)" -batchfile 1.txt

在获得了 p q之后,根据原理,算出n的欧拉函数φ(n)=(p-1)*(q-1),然后求φ(n)对n的模逆解出d,再根据密文和私钥(n,d)反推出明文。

例题https://dn.jarvisoj.com/challengefiles/mediumRSA.rar.07aab25c9c54464a8c9821ca28503330

本题拿到一组公钥证书和加密后的文件,用刚才介绍过的方法打开提取信息

对于简单情况,使用yafu分解得出p q

然后根据基本原理求解明文即可,本题完整解题脚本如下

from Crypto.PublicKey import RSA
import gmpy2
from Crypto.Util.number import long_to_bytes
r=open('CTFquestion/RSA3/pubkey.pem').read()
pub=RSA.importKey(r)
#读取pem文件,获取n,e
n=pub.n
e=pub.e
#读取enc文件,获取c
flag_encode= open('CTFquestion/RSA3/flag.enc','rb').read()
c=int(flag_encode.hex(),16)
#p,q由yafu解的
p=275127860351348928173285174381581152299
q=319576316814478949870590164193048041239
#求e对φ(n)的模逆
d=gmpy2.invert(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)))

  • 低加密指数攻击

适用情况e 过小,一般来说 n,c都较大。

原理:根据公钥加密机制,密文c=m^e mod n。如果选取的 e 过小,加密指数过低,就会引发安全问题

如果 e 过小导致 m ^e < n,可以推出 c = m^e,此时直接对密文开e次方即可得出明文

对于 m^e > n,根据j加密公式变形得:m^e= k*n+c(k为正整数),由此,我们可以通过爆破k来获得m^e,之后再开根号即可获得明文

例题:https://dn.jarvisoj.com/challengefiles/extremelyhardRSA.rar.8782e822c895a2af3d8ba4ffbb3e280b

获取信息发现 n,c长的过分,e=3

爆破k脚本如下(大概需要运行5-10分钟)

from Crypto.PublicKey import RSA
import base64
import gmpy2
from Crypto.Util.number import *
r=open('../CTFquestion/RSA/pubkey.pem').read()
pub=RSA.importKey(r)
#读取pem文件,获取n,e
n=pub.n
e=pub.e
print("n:",pub.n)
print("e:",pub.e)
#读取enc文件,获取c
flag_encode= open('../CTFquestion/RSA/flag.enc','rb').read()
print("c:",int(flag_encode.hex(),16))
c=int(flag_encode.hex(),16)
k=1
#枚举开方结果,iroot方法为求某个数的某次方,返回(开方结果(int),能否开整数次方(bool))
while 1:
    m,f=gmpy2.iroot(n*k+c,e)
    k+=1
    if f:
        print(m)
        print(long_to_bytes(str(m)))

  • 低加密指数广播攻击

适用情况:e很小,给出多组 n 和 c相对应的情况

原理:根据已有信息可列方程组

c1 = m^e mod n1
c2 = m^e mod n2

c3 = m^e mod n3

则可在满足 n1,n2,n3两两互质的前提下引入中国剩余定理(孙子定理)求解 m^e,关于中国剩余定理,以下内容摘自百度百科

中国剩余定理给出了以下的一元线性同余方程组:

一般攻击方法:

设共有 i 组方程

  1. 计算出M:所有n的乘积

  2. 对于每一个ni计算出mi:mi=M/ni

  3. 计算每一个mi对ni的模逆ti:ti*mi % ni=1   (a,ti,k=gmpy2.gcdext(mi,ni))

  4. 计算每一个mi*ti*ci的值,累加起来对M取模得到m^e

  5. 开e次方得到明文m

例题暂鸽,原来做的题找不到了,以后找到了再补

  • 低解密指数攻击(Wiener attack)

适用情况:e很大,一般n,c也很大,难以分解

原理:与低加密指数攻击类似,由于d 是 e对 φ(n) 的模反元素,因此 e 如果过大势必会引起 d 过小,即解密所用指数过小,同样会引发安全问题。类似的体型涉及连分数展开和渐进方程计算,感兴趣的朋友可以自行查阅,由于证明过程过于繁琐就不做过多展开。下面只介绍通过Wiener Attack可以在多项式时间中分解n的方法。

例题:https://ctf.bugku.com/files/98e8f374f63ee3ef4818621ceafcb78f/rsa.txt

解法1:用工具!

RsaCtfTool是专为CTFer开发的一款RSA攻击软件,Kali下安装可参考此文Ubuntu下RsaCtfTool的安装及使用 - FreeBuf网络安全行业门户

RsaCtfTool内部捆绑了Wiener attack的相关插件,使用方法及主要命令如下

之后即可获得p,q

解法2:写脚本!(用到连分数展开算法和渐进分数算法)

from Crypto.Util.number import long_to_bytes
e=354611102441307572056572181827925899198345350228753730931089393275463916544456626894245415096107834465778409532373187125318554614722599301791528916212839368121066035541008808261534500586023652767712271625785204280964688004680328300124849680477105302519377370092578107827116821391826210972320377614967547827619
n=460657813884289609896372056585544172485318117026246263899744329237492701820627219556007788200590119136173895989001382151536006853823326382892363143604314518686388786002989248800814861248595075326277099645338694977097459168530898776007293695728101976069423971696524237755227187061418202849911479124793990722597
c=38230991316229399651823567590692301060044620412191737764632384680546256228451518238842965221394711848337832459443844446889468362154188214840736744657885858943810177675871991111466653158257191139605699916347308294995664530280816850482740530602254559123759121106338359220242637775919026933563326069449424391192

#连分数展开算法
def lf(x,y):
    arr=[]
    while y:
        arr+=[x//y]
        x,y=y,x%y
    return arr

def jj(k):
    x=0
    y=1
    for i in k[::-1]:
        x,y=y,x+i*y
    return (y,x)
data=lf(e,n)
for x in range(1,len(data)+1):
    data1=data[:x]
    d = jj(data1)[1]
    print(long_to_bytes(str(pow(c,d,n))))


将算出的结果复制到txt里搜索一下flag即可

  • 共模攻击

适用情况:多组密文使用同一个模数n加密的情况,不同e之间两两互质

原理

首先介绍扩展欧几里得算法:

 我们知道给予二整数 a 与 b, 必存在有整数 x 与 y 使得ax + by = gcd(a,b)。若在计算最大公约数过程中使用辗转相除法并收集辗转相除法中产生的式子,倒回去,可以得到ax+by=gcd(a,b)的整数解。

实现代码

def ext_euclid(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, q = ext_euclid(b, a % b)
        # q = gcd(a, b) = gcd(b, a%b)
        x, y = y, (x - (a // b) * y)
        return x, y, q

共模攻击可行性推导过程:

根据贝祖公式:e1*s1+e2*s2 = 1 (s1 s2 为一正一负的整数)
由于:c1 = m^e1%n c2 = m^e2%n
∴(c1^s1*c2^s2)%n = ((m^e1%n)^s1*(m^e2%n)^s2)%n
∴(c1^s1*c2^s2)%n = ((m^e1%n)^s1*(m^e2%n)^s2)%n (c1^s1*c2^s2)%n = ((m^e1)^s1*(m^e2)^s2)%n
∴(c1^s1*c2^s2)%n = (m^(e1^s1+e2^s2))%n

∵e1*s1+e2*s2 = 1
∴(c1^s1*c2^s2)%n = (m^(1))%n (c1^s1*c2^s2)%n = m^%n
c1^s1*c2^s2 = m

基本攻击手法:

根据扩展欧几里得算法,给定e1,e2,求解出s1,s2,然后根据推导得出的公式计算m

例题https://dn.jarvisoj.com/challengefiles/veryhardRSA.rar.2a89300bd46d028a14e2b5752733fe98

第一步,从给出的py文件中获得到N和两个互质的e

第二步,提取c1,c2,编写脚本使用扩展欧几里得算法求解m。需要注意的是,在数论模运算中,要求一个数的负数次幂,与常规方法并不一样。 比如此处要求c2的s2次幂,就要先计算c2的模反元素c2r,然后求c2r的-s2次幂。完整代码如下

from Crypto.PublicKey import RSA
import gmpy2
from Crypto.Util.number import long_to_bytes
#扩展欧几里得算法
def ext_euclid(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, q = ext_euclid(b, a % b)
        # q = gcd(a, b) = gcd(b, a%b)
        x, y = y, (x - (a // b) * y)
        return x, y, q
n=1#n过长,贴代码超长了,使用的时候用你的N替换
e1=17
e2=65537
flag_encode= open('CTFquestion/RSA4/flag.enc1','rb').read()
c1=int(flag_encode.hex(),16)
flag_encode= open('CTFquestion/RSA4/flag.enc2','rb').read()
c2=int(flag_encode.hex(),16)
print("c1:",c1)
print("c2:",c2)
s1=ext_euclid(e1,e2)[0]
s2=ext_euclid(e1,e2)[1]
if s1<0:
    s1=-s1
    c1=gmpy2.invert(c1,n)
if s2<0:
    s2=-s2
    c2=gmpy2.invert(c2,n)
m=long_to_bytes(str(pow(c1,s1,n)*pow(c2,s2,n)%n))
print(m)


  • 类RSA加密,Rabin算法

适用情况:e=2,有可能是Rabin算法

原理:Rabin也是一种非对称加密算法,与RSA依赖大整数分解的复杂性不同的是,Rabin算法的可靠性是建立在计算模合数平方根困难性上,关于Rabin算法的详细解释摘自这位大佬的博文https://www.cnblogs.com/iptables/p/5598049.html

破解方式:Rabin在处理过程中与RSA有诸多相似之处,比如二者都会用到中国剩余定理和扩展欧几里得算法,特别是二者公钥结构十分相似,所以经常伪装成RSA出现,大部分情况下,如果看到e=2,可以首先怀疑是Rabin算法。破解密文只需要按照解密公式推出yp,yq,mp,mq,再根据公式求解m组即可(Rabin求解结果是一个四元组,在日常生活中解码还需要额外参数,但是在CTF中对于四个结果每一个尝试一下即可)

例题:https://dn.jarvisoj.com/challengefiles/hardRSA.rar.b498edae4e73af8eb4567fb18117de46

直接上代码

from Crypto.PublicKey import RSA
#扩展欧几里得算法 ax+by=gcd(a,b)  传入a、b,返回x,y以及gcd(a,b)
def ext_euclid(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, q = ext_euclid(b, a % b)
        # q = gcd(a, b) = gcd(b, a%b)
        x, y = y, (x - (a // b) * y)
        return x, y, q
r=open('../CTFquestion/RSA2/pubkey.pem').read()
pub=RSA.importKey(r)
#读取pem文件,获取n,e
n=pub.n
e=pub.e
#读取enc文件,获取c
flag_encode= open('../CTFquestion/RSA2/flag.enc','rb').read()
c=int(flag_encode.hex(),16)
#p,q通过yafu分解得到
p=int(275127860351348928173285174381581152299)
q=int(319576316814478949870590164193048041239)
yp,yq,k=ext_euclid(p,q)
mp=pow(c,(p+1)//4,p)
mq=pow(c,(q+1)//4,q)
r1=(yp*p*mq+yq*q*mp)%n
r2=n-int(r1)
r3=(yp*p*mq-yq*q*mp)%n
r4=n-int(r3)

print(hex(r1))
print(hex(r2))
print(hex(r3))
print(hex(r4))
for i in (r1,r2,r3,r4):
    s = '%x' % i
    if len(s) % 2 != 0:
        s = '0' + s
    # print(long_to_bytes(int(s,16)))
    print(str(bytearray.fromhex(s)))

  • 5
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
RSA正确性证明: RSA算法的正确性基于数论中的两个重要结论:欧拉定理和费马小定理。 欧拉定理:对于任何正整数a、n,如果a和n互质,那么a的欧拉函数φ(n)满足a^φ(n)≡1(mod n)。 费马小定理:对于任何素数p和任何整数a,a^(p-1) ≡ 1 (mod p)。 RSA算法的正确性可以通过以下步骤进行证明: 1. 选择两个大素数p和q,并计算它们的乘积n=p*q。 2. 计算n的欧拉函数φ(n)=(p-1)*(q-1)。 3. 找到一个整数e,使得e与φ(n)互质,即gcd(e,φ(n))=1。 4. 计算e的模反元素d,使得e*d ≡ 1 (mod φ(n))。 5. 公钥为(n,e),私钥为(n,d)。 加密过程:将明文m进行加密,得到密文c:c ≡ m^e (mod n)。 解密过程:将密文c进行解密,得到明文m:m ≡ c^d (mod n)。 正确性证明:根据费马小定理,c^d ≡ (m^e)^d ≡ m^(e*d) ≡ m^(k*φ(n)+1) ≡ m * m^(k*φ(n)) (mod n),其中k为任意整数。由于m和n互质,因此根据欧拉定理得到m^φ(n) ≡ 1 (mod n),所以m^(k*φ(n)) ≡ 1 (mod n)。因此,c^d ≡ m (mod n),即RSA算法是正确的。 RSA主要的攻击手段: 1. 小质数攻击:如果使用的素数p或q太小,攻击者可以通过分解n来获得私钥。因此,RSA算法要求使用足够大的素数。 2. 常模攻击攻击者可以通过分析加密后密文的模数n来获得私钥信息。为了防止此类攻击RSA算法需要对明文进行填充。 3. 选择明文攻击:如果加密过程中使用相同的明文加密多次,攻击者可以通过观察密文之间的差异来获得私钥。为了防止此类攻击RSA算法需要使用随机填充和随机数。 4. 时间攻击攻击者可以通过观察加密或解密过程中的时间来破解私钥。为了防止此类攻击RSA算法需要使用恒定时间算法
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值