1、变量涉及
明文:m
密文:c
模数:n
大质数:p,q
欧拉函数值:r
密钥:d,e
2、算法流程
1、已知n、e或是p、q、e求d
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
# -*- 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
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')=
攻击脚本