RSA攻击持续总结

RSA攻击持续总结

RSA算法描述

1、变量涉及
明文:m
密文:c
模数:n
大质数:p,q
欧拉函数值:r
密钥:d,e
2、算法流程

  • 随机生成两个大质数p,q
  • n=p*q
  • r=(p-1)*(q-1)
  • 求e:1
  • 求d:1
  • 加密过程:m
  • 解密过程:m=pow(c,d,n)

RSA攻击

1、已知n、e或是p、q、e求d

  • 对模数n的分解
    yafu factor()函数
    分解n网站
  • 得到p、q,利用脚本
import gmpy2
p = 473398607161
q = 4511491
e = 17
print gmpy2.invert(e, (p-1)*(q-1))

2、已知c、d、n求m
直接利用m=pow(c,d,n)

3、已知c、e、n求m
即需要先求出密钥d,使用1中的方法,再使用2中的方法

4、已知私钥文件、c求m
题目中给出了私钥文件private.pem和flag.enc
可在kali或是Ubuntu使用openssl直接进行解密

openssl rsautl -decrypt -in flag.enc -inkey private.pem

5、已知公钥文件、c求m
题目中给出了public.pem和密文flag.enc
使用openssl rsa -pubin -text -modulus -in warmup -in pubkey.pem
返回公钥信息,即可以得到n、e,分解n得到p、q
使用rsatool生成私钥文件: private.pem

shell python rsatool.py -o private.pem -e 65537 -p XXX -q XXX
即接下来用生成的私钥文件解密flag文件

shell openssl rsautl -decrypt -in flag.enc -inkey private.pem

6、共模攻击
所谓共模攻击指的是两个不同的e1,e2和一个n对一个明文m进行加密,得到两份密文c1,c2,此时可以在不分解n的情况下还原出明文m的值。
c1 = me1 mod n
c2 = me2 mod n
RSA攻击持续总结_第1张图片

# -*- coding: cp936 -*-
import time
import gmpy2
n = 
e = [665213, 368273]
c = [...L, ...L]
print '[+]Detecting m...'
time.clock()
c1 = c[0]
c2 = c[1]
e1 = e[0]
e2 = e[1]
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = s[2]
# 求模反元素
if s1 < 0:
    s1 = -s1
    c1 = gmpy2.invert(c1, n)
elif s2 < 0:
    s2 = -s2
    c2 = gmpy2.invert(c2, n)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
print '  [-]m is:' + '{:x}'.format(int(m)).decode('hex')
print '\n[!]Timer:', round(time.clock(),2), 's'
print '[!]All Done!'

import gmpy2 as gp
import libnum
def exgcd(a, b):
    if b==0:
        return 1, 0, a
    x2, y2, r = exgcd(b, a%b)
    x1 = y2
    y1 = x2-(a//b)*y2
    return x1, y1, r
 
n=gp.mpz()
c1=gp.mpz()
e1=gp.mpz()
c2=gp.mpz()
e2=gp.mpz()

r1, r2, t = exgcd(e1, e2)
m = gp.powmod(c1, r1, n) * gp.powmod(c2, r2, n) % n
print m 
print hex(m)[2:] 
print libnum.n2s(m)

7、低加密指数攻击
题目中给出了pubkey.pem和密文flag.enc
使用openssl命令提取其中的n和e,得到的e十分小。e是加密者自定义的,一般会越大越安全,即这里是低加密。

有一种情况是当e = 3时的小明文攻击
当e = 3时,如果明文过小,即m的三次方还是小于n,可以导致 c = me,e = 3 则此时直接对密文开三次方即可

import gmpy2
e = 
n = 
c = 

print 'n=', n
print 'c=', c
print '[+]Detecting m...'
result = gmpy2.iroot(c, 3)

print '  [-]The c has cubic root?', result[1]

if result[1]: print '  [-]The m is:', '{:x}'.format(result[0]).decode('hex')

print '[!]All Done!'

还有一种情况是明文m的三次方比n大,但是不是足够大,这时可以设k,有:
c = me + kn
爆破k,如果c - kn能开三次根式,那么可以直接得到明文

# -*- coding: cp936 -*-
import gmpy2, time
e = 3
# 读入 n, 密文
n = 
c = 
i = 239000000   # i 应该是未知的。这里缩短一下距离, 防止跑得太久
print 'n=', n
print 'c=', c
print '[!]Done!\n'
print '[+]Detecting m...'
s = time.clock()
while 1:
    m, b = gmpy2.iroot(c + i * n, 3)
    if b:
        print '  [-]m is: ' + '{:x}'.format(int(m)).decode('hex')
        break
    #print '  [-]i = %d\r' % i,
    i += 1
print '[!]Timer:', round(time.clock() - s, 2), 's'

8、低加密指数广播攻击
这里加密指数比较低,并且使用了相同的加密指数给一个接受群发消息,这时可以根据广播攻击的得到明文。
这里取e = 3
c₁ = me mod n₁
c₂ = me mod n₂
c₃ = me mod n₃
运用中国剩余定理,当e = 3时
c = m3 mod n₁n₂n₃

# -*- coding: cp936 -*-
import gmpy2
import time
def CRT(items):
    N = reduce(lambda x, y: x * y, (i[1] for i in items))
    result = 0
    for a, n in items:
        m = N / n
        d, r, s = gmpy2.gcdext(n, m)
        if d != 1: raise Exception("Input not pairwise co-prime")
        result += a * s * m
    return result % N, N
# 读入 e, n, c
e = 
n = [...L,...L] #n是一个数组因为加密的是群消息
c = [...L,...L]
print '[+]Detecting m...'
data = zip(c, n)
x, n = CRT(data)
realnum = gmpy2.iroot(gmpy2.mpz(x), e)[0].digits()
print '  [-]m is: ' + '{:x}'.format(int(realnum)).decode('hex')
print '[!]All Done!'

9、低解密指数攻击
与低加密指数攻击相比,这里的e过大,直接使用RSAwienerHacker.py,求出d,接下来再求出m。
这个脚本在github上,脚本链接

这里给出上次发现的别人写的代码

# -*- coding: cp936 -*-
import gmpy2
import time
# 展开为连分数
def continuedFra(x, y):
    cF = []
    while y:
        cF += [x / y]
        x, y = y, x % y
    return cF
def Simplify(ctnf):
    numerator = 0
    denominator = 1
    for x in ctnf[::-1]:
        numerator, denominator = denominator, x * denominator + numerator
    return (numerator, denominator)
# 连分数化简
def calculateFrac(x, y):
    cF = continuedFra(x, y)
    cF = map(Simplify, (cF[0:i] for i in xrange(1, len(cF))))
    return cF
# 解韦达定理
def solve_pq(a, b, c):
    par = gmpy2.isqrt(b * b - 4 * a * c)
    return (-b + par) / (2 * a), (-b - par) / (2 * a)
def wienerAttack(e, n):
    for (d, k) in calculateFrac(e, n):
        if k == 0: continue
        if (e * d - 1) % k != 0: continue
        phi = (e * d - 1) / k
        p, q = solve_pq(1, n - phi + 1, n)
        if p * q == n:
            return abs(int(p)), abs(int(q))
    print 'not find!'
time.clock()
n = 
e = 
c = 
p, q = wienerAttack(e, n)
print '[+]Found!'
print '  [-]p =',p
print '  [-]q =',q
print '  [-]n =',p*q
d = gmpy2.invert(e,(p-1)*(q-1))
print '  [-]d =', d
print '  [-]m is:' + '{:x}'.format(pow(c,d,n)).decode('hex')
print '\n[!]Timer:', round(time.clock(),2), 's'
print '[!]All Done!'

10、n的公约数
题目中给出了两个n1,n2一个共同的密钥e,两个密文c1,c2
这里可以运用两个n的公因子q,再通过q和n1求出p1,已知p1和q,可以求得d,即接下来已知c1,d1,n1可以求得明文。

from Crypto.Util.number import getPrime,bytes_to_long,long_to_bytes

c1=
n1=
n2=
e=

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 modinv(a,m):
    g,x,y=egcd(a,m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x%m
def gcd(a,b):
    while a != 0:
        a,b=b%a,a
    return b
q=gcd(n1,n2)
p1=n1/q

d=modinv(e,(p1-1)*(q-1))
dec1=pow(c1,d,n1)
print long_to_bytes(dec1)

11、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 modinv(a,m):
    g,x,y=egcd(a,m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x%m
e=
p=
q=
r=
n=
d=modinv(e,(p-1)*(q-1)*(r-1))
print(d)
c=
destr=hex(pow(c,d,n))
destr=destr[2:-1]
print(destr)

12、dp泄露攻击

import gmpy2
import libnum

e = 
n = 
dp = 
c = 

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

print libnum.n2s(pow(c,d,n))

13、dp,dq泄露攻击

import gmpy2
import libnum

p =  
q = 
dp =  
dq =  
c = 

InvQ = gmpy2.invert(q,p)
mp = pow(c,dp,p)
mq = pow(c,dq,q)
m = (((mp-mq)*InvQ) % p)*q+mq
print libnum.n2s(m)

14、已知n、r求p、q的算法
这里自己写了一个用二分法求二元一次方程组的代码

n=
r=
c1=n-r+1
print c1

l=c1/2
r=c1
p=(l+r)/2
y=p*(c1-p)

while l<r:
	p=(l+r)/2
	y=p*(c1-p)
        if y==n:
            print p
            break
        if y>n:
			print 'y>n'
			l=p
        else:
			print 'y
			r=p
        print 'done'
        
q=c1-p

print q

15、已知n、e*d,求p,q算法
RSA攻击持续总结_第2张图片

2020MRCTF中的一道题目
RSA攻击持续总结_第3张图片
参考一些大佬写的脚本

Q_n = 20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947
Q_E_D = 100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201
f, s, tem = Q_E_D-1, 0, 1
while f % 2 == 0:
    f = f // 2
    s += 1
i, a, t = s, 2, f
b = pow(a, t, Q_n)
while b == 1:
    a = sympy.nextprime(a)
    b = pow(a, t, Q_n)

while i != 1:
    c = pow(b, 2, Q_n)
    if c != 1:
        b = c
        i -= 1
    else:
        break
if b == Q_n-1:
    a = sympy.nextprime(a)
    b = pow(a, t, Q_n)
    while b == 1:
        a = sympy.nextprime(a)
        b = pow(a, t, Q_n)

p = gcd(b-1, Q_n)
q = Q_n//p

原文链接

#coding=utf-8
from random import randint
import gmpy2
def oddR(r):
    while r%2==0:
        r=r//2
    return r
    
def bits(b):
    k=[]
    while b:
        if b%2!=0:
            k.append(1)
        else:
            k.append(0)
        b>>=1
    k.reverse()      
    return k
    
def quickmod(a,b,n):      #a^b mod n 快速幂模n运算
    f=1
    k=bits(b)
    for i in range(len(k)):
        f=(f*f)%n
        if k[i]:
            f=(f*a)%n
    return f

def gcd(m,n):
    while(n!=0):
        m,n=n,m%n
    return m

def func(e_d,N):
    k=e_d-1            
    r=oddR(k)           #求出k=2^t*r中的r
    
    while True:
        b=randint(2,N-1)    #获取区间(2,N-1)的一个随机数
        a=quickmod(b,r,N)   
        if a==1:            
            continue    
        y=gcd(a-1,N)
        if a>1 and y>1:    
            q=N//y
            return q
        else:
            r=r*2         
    
def deciphering(e_d,n):    、
    p=func(e_d,n)
    q=n//p
    phi=n-(p+q)+1
    if p*q==n:
        print p
        print q
    else:
        print"error"

n =  
e_d=  
deciphering(e_d,n)

原文链接

16、已知(e, n, d)分解N

from gmpy2 import next_prime, gcd
def Factorize(n, e, d):
    g = 2
    while True:
        k = e * d - 1
        while not k & 1:
            k //= 2
            p = int(gcd(pow(g, k, n) - 1, n)) % n
            if p > 1:
                return (p, n // p)
        g = int(next_prime(g))
if __name__ == "__main__":
    n = 
    e = 
    d = 
    print(Factorize(n, e, d))

17、coppersmith算法
以下内容是看的是一位博主的文章, 原文章
明文高位已知
例2019强网杯

[+]Generating challenge 1

[+]n=0xa1888c641a05aeb81b3d1686317a86f104791fe1d570a5b11209f45d09ea401d255a70744e7a2d39520e359c23a9f1209ee47f496dbd279e62ee1648b3a277ced8825298274322e0a7a86deea282676310a73b6bb946fc924c34ac6c8784ff559bf9a004c03fb167ef54aaea90ce587f2f3074b40d7f632022ec8fb12e659953L
[+]e=3
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x93145ece45f416a11e5e9475518f165365456183c361500c2f78aff263028c90f20b7d97205f54e21f3bcc8a556b457889fde3719d0a0f9c9646f3f0d0a1e4bee0f259f023168fe8cc0511848c1635022fcc20b6088084585e2f8055a9d1b1d6bdb228087900bf7c6d42298f8e45c451562c816e2303990834c94e580bf0cbd1L
[+]((m>>72)<<72)=0x9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b000000000000000000L

代码:(需要使用sage)

e = 0x3
b = 0x9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b000000000000000000L
n = 0xa1888c641a05aeb81b3d1686317a86f104791fe1d570a5b11209f45d09ea401d255a70744e7a2d39520e359c23a9f1209ee47f496dbd279e62ee1648b3a277ced8825298274322e0a7a86deea282676310a73b6bb946fc924c34ac6c8784ff559bf9a004c03fb167ef54aaea90ce587f2f3074b40d7f632022ec8fb12e659953L
c=0x93145ece45f416a11e5e9475518f165365456183c361500c2f78aff263028c90f20b7d97205f54e21f3bcc8a556b457889fde3719d0a0f9c9646f3f0d0a1e4bee0f259f023168fe8cc0511848c1635022fcc20b6088084585e2f8055a9d1b1d6bdb228087900bf7c6d42298f8e45c451562c816e2303990834c94e580bf0cbd1L
kbits=72
PR.<x> = PolynomialRing(Zmod(n))
f = (x + b)^e-c
x0 = f.small_roots(X=2^kbits, beta=1)[0]
print "x: %s" %hex(int(x0))

p的高位已知,低位未知
例2019强网杯

[+]Generating challenge 2
[+]n=0x241ac918f708fff645d3d6e24315e5bb045c02e788c2b7f74b2b83484ce9c0285b6c54d99e2a601a386237d666db2805175e7cc86a733a97aeaab63486133103e30c1dca09741819026bd3ea8d08746d1d38df63c025c1793bdc7e38d194a30b492aadf9e31a6c1240a65db49e061b48f1f2ae949ac9e7e0992ed24f9c01578dL
[+]e=65537
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x1922e7151c779d6bb554cba6a05858415e74739c36df0bcf169e49ef0e566a4353c51a306036970005f2321d1d104f91a673f40944e830619ed683d8f84eaf26e7a93c4abe1dbd7ca3babf3f4959def0e3d87f7818d54633a790fc74e9fed3c5b5456c21e3f425240f6217b0b14516cb59aa0ce74b83ca17d8cc4a0fbc829fb8L
[+]((p>>128)<<128)=0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a873200000000000000000000000000000000L

代码 :(需要使用sage)

n = 0x241ac918f708fff645d3d6e24315e5bb045c02e788c2b7f74b2b83484ce9c0285b6c54d99e2a601a386237d666db2805175e7cc86a733a97aeaab63486133103e30c1dca09741819026bd3ea8d08746d1d38df63c025c1793bdc7e38d194a30b492aadf9e31a6c1240a65db49e061b48f1f2ae949ac9e7e0992ed24f9c01578dL
p_fake = 0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a873200000000000000000000000000000000L
 
pbits = 1024
kbits = 130
pbar = p_fake & (2^pbits-2^kbits)
print "upper %d bits (of %d bits) is given" % (pbits-kbits, pbits)
 
PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar
 
x0 = f.small_roots(X=2^kbits, beta=0.4)[0]  # find root < 2^kbits with factor >= n^0.3
print hex(int(x0 + pbar))

已知低位的密钥和N
Partial Key Exposure Attack(部分私钥暴露攻击)
2019强网杯

[+]Generating challenge 3
[+]n=0x51fb3416aa0d71a430157d7c9853602a758e15462e7c08827b04cd3220c427bbb8199ed4f5393dae43f013b68732a685defc17497f0912c886fa780dfacdfbb1461197d95a92a7a74ade874127a61411e14a901382ed3fb9d62c040c0dbaa374b5a4df06481a26da3fca271429ff10a4fc973b1c82553e3c1dd4f2f37dc24b3bL
[+]e=3
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x3d7e16fd8b0b1afdb4e12594c3d8590f1175800ef07bb275d7a8ad983d0d5d5fd5c6f81efa40f5d10c48bb200f805e679d633ee584748e5feef003e0921dea736ba91eef72f3d591d3a54cd59fd36f61140fdd3fb2e2c028b684e50cbeae4a1f386f6ab35359d46a29996c0f7d9a4a189f1096150496746f064c3cc41cf111b0L
[+]d=invmod(e,(p-1)*(q-1))
[+]d&((1<<512)-1)=0x17c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL
[-]long_to_bytes(m).encode('hex')=
d = 0x36a7780f1c08f66d7563a8fdbae2401c4e5eb8d97452b056fcadde216b2d6fd27abbbf38a37b7e742d4ab7cf04cc6f03e9fd64dbaa060c85af51a55ea733fd2017c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL

代码:(需要使用sage)

def partial_p(p0, kbits, n):
    PR.<x> = PolynomialRing(Zmod(n))
    nbits = n.nbits()
    f = 2^kbits*x + p0
    f = f.monic()
    roots = f.small_roots(X=2^(nbits//2-kbits), beta=0.3)  # find root < 2^(nbits//2-kbits) with factor >= n^0.3
    if roots:
        x0 = roots[0]
        p = gcd(2^kbits*x0 + p0, n)
        return ZZ(p)


def find_p(d0, kbits, e, n):
    X = var('X')


    for k in xrange(1, e+1):
        results = solve_mod([e*d0*X - k*X*(n-X+1) + k*n == X], 2^kbits)
        for x in results:
            p0 = ZZ(x[0])
            p = partial_p(p0, kbits, n)
            if p:
                return p


if __name__ == '__main__':
    # n = 0x51fb3416aa0d71a430157d7c9853602a758e15462e7c08827b04cd3220c427bbb8199ed4f5393dae43f013b68732a685defc17497f0912c886fa780dfacdfbb1461197d95a92a7a74ade874127a61411e14a901382ed3fb9d62c040c0dbaa374b5a4df06481a26da3fca271429ff10a4fc973b1c82553e3c1dd4f2f37dc24b3bL
    e = 3
    # d = 0x17c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL

    n = 57569201048993475052349187244752169754165154575782760003851777813767048953051839288528137121670999884309849815765999616346303792471518639080697166767644957046582385785721102370288806038187956032505761532789716009522131450217010629338000241936036185205038814391205848232364006349213836317806903032515194407739
    nbits = n.nbits()
    kbits = floor(nbits*0.5)
    print "kbits : ", kbits 
    d0 = 1244848677959253796774387650148978357579294769878147704641867595620534030329181934099194560059806799908134954814673426128260540575360296026444649631806619
    print "lower %d bits (of %d bits) is given" % (kbits, nbits)

    p = find_p(d0, kbits, e, n)
    print "found p: %d" % p
    q = n//p
    # print d
    print inverse_mod(e, (p-1))

18、Boneh and Durfee attack
当 d 较小时,满足d < N^{0.292} 时,我们可以利用该攻击,比 Wiener’s Attack 要强一些。
例题
2019强网杯

[+]Generating challenge 6
[+]n=0xbadd260d14ea665b62e7d2e634f20a6382ac369cd44017305b69cf3a2694667ee651acded7085e0757d169b090f29f3f86fec255746674ffa8a6a3e1c9e1861003eb39f82cf74d84cc18e345f60865f998b33fc182a1a4ffa71f5ae48a1b5cb4c5f154b0997dc9b001e441815ce59c6c825f064fdca678858758dc2cebbc4d27L
[+]d=random.getrandbits(1024*0.270)
[+]e=invmod(d,phin)
[+]hex(e)=0x11722b54dd6f3ad9ce81da6f6ecb0acaf2cbc3885841d08b32abc0672d1a7293f9856db8f9407dc05f6f373a2d9246752a7cc7b1b6923f1827adfaeefc811e6e5989cce9f00897cfc1fc57987cce4862b5343bc8e91ddf2bd9e23aea9316a69f28f407cfe324d546a7dde13eb0bd052f694aefe8ec0f5298800277dbab4a33bbL
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0xe3505f41ec936cf6bd8ae344bfec85746dc7d87a5943b3a7136482dd7b980f68f52c887585d1c7ca099310c4da2f70d4d5345d3641428797030177da6cc0d41e7b28d0abce694157c611697df8d0add3d900c00f778ac3428f341f47ecc4d868c6c5de0724b0c3403296d84f26736aa66f7905d498fa1862ca59e97f8f866cL
[-]long_to_bytes(m).encode('hex')=

攻击脚本

你可能感兴趣的:(RSA攻击持续总结)