第七届“湖湘杯”Crypto的signin
下载附件,打开查看题目逻辑代码:
from Crypto.Util.number import *
from secret import flag
import random
m1 = bytes_to_long(flag[:len(flag) // 2])
m2 = bytes_to_long(flag[len(flag) // 2:])
def gen(pbits, qbits):
p1, q1 = getPrime(pbits), getPrime(qbits)
n1 = p1**4*q1
q2 = getPrime(qbits)
bound = p1 // (8*q1*q2) + 1
p2 = random.randrange(p1, p1 + bound)
while not isPrime(p2):
p2 = random.randrange(p1, p1 + bound)
n2 = p2**4*q2
return (n1, n2), (p1, q1), (p2, q2)
e = 0x10001
pbits = int(360)
qbits = int(128)
pk, sk1, sk2 = gen(pbits, qbits)
c1 = pow(m1, e, pk[0])
c2 = pow(m2, e, pk[1])
print(f'pk = {pk}')
print(f'c1, c2 = {c1, c2}')
"""
pk = (1150398070565459492080597718626032792435556703413923483458704675295997646493249759818468321328556510074044954676615760446708253531839417036997811506222349194302791943489195718713797322878586379546657275419261647635859989280700191441312691274285176619391539387875252135478424580680264554294179123254566796890998243909286508189826458854346825493157697201495100628216832191035903848391447704849808577310612723700318670466035077202673373956324725108350230357879374234418393233, 1242678737076048096780023147702514112272319497423818488193557934695583793070332178723043194823444815153743889740338870676093799728875725651036060313223096288606947708155579060628807516053981975820338028456770109640111153719903207363617099371353910243497871090334898522942934052035102902892149792570965804205461900841595290667647854346905445201396273291648968142608158533514391348407631818144116768794595226974831093526512117505486679153727123796834305088741279455621586989)
c1, c2 = (361624030197288323178211941746074961985876772079713896964822566468795093475887773853629454653096485450671233584616088768705417987527877166166213574572987732852155320225332020636386698169212072312758052524652761304795529199864805108000796457423822443871436659548626629448170698048984709740274043050729249408577243328282313593461300703078854044587993248807613713896590402657788194264718603549894361488507629356532718775278399264279359256975688280723740017979438505001819438, 33322989148902718763644384246610630825314206644879155585369541624158380990667828419255828083639294898100922608833810585530801931417726134558845725168047585271855248605561256531342703212030641555260907310067120102069499927711242804407691706542428236208695153618955781372741765233319988193384708525251620506966304554054884590718068210659709406626033891748214407992041364462525367373648910810036622684929049996166651416565651803952838857960054689875755131784246099270581394)
"""
.
.
大体分析一下,题目先是把 flag 分成两部分,然后分别生成两个大素数
3608bit 的 p1,1288bit 的 p2。
然后继续生成了两个相邻
的大素数q1、q2,用p1的 4 次方和 q1相乘得到N1,N2则是用 p2 的 4 次方和 q2 相乘,再用一个比较小的 E 加密。
(其实根据题目中涉及指数的N n1 = p1**4*q1
和 n2 = p2**4*q2
就可以发现和 2020 年羊城杯 Crypto 的 RRRRRRRSA
有很大的相似性。
可以参考博客:https://blog.csdn.net/xiao__1bai/article/details/121576940 )
因为题目只给了N1
、N2
和 C1
、C2
。所以我们从这两对数来寻找突破口,可以发现N1和N2的大小和很相近
的,可以尝试一下 wiener attack
攻击。
.
.
按wiener attack 攻击流程展开:
由于(p1/p2)**4
接近于1
,所以 N1/N2
的比例很相近于q1/q2
。
所以在 q1/q2
在区间(N1/N2,1)
之间 (这是关键,不用P1/P2是因为4次方不好求)
尝试对 N1/N2 进行连分数展开
并求其各项
渐进分数,其中某个连分数的分子可能就是q1。
.
.
.
修改以前的连分数低解密指数脚本:
import gmpy2
N1, N2 = (
1150398070565459492080597718626032792435556703413923483458704675295997646493249759818468321328556510074044954676615760446708253531839417036997811506222349194302791943489195718713797322878586379546657275419261647635859989280700191441312691274285176619391539387875252135478424580680264554294179123254566796890998243909286508189826458854346825493157697201495100628216832191035903848391447704849808577310612723700318670466035077202673373956324725108350230357879374234418393233,
1242678737076048096780023147702514112272319497423818488193557934695583793070332178723043194823444815153743889740338870676093799728875725651036060313223096288606947708155579060628807516053981975820338028456770109640111153719903207363617099371353910243497871090334898522942934052035102902892149792570965804205461900841595290667647854346905445201396273291648968142608158533514391348407631818144116768794595226974831093526512117505486679153727123796834305088741279455621586989)
c1, c2 = (
361624030197288323178211941746074961985876772079713896964822566468795093475887773853629454653096485450671233584616088768705417987527877166166213574572987732852155320225332020636386698169212072312758052524652761304795529199864805108000796457423822443871436659548626629448170698048984709740274043050729249408577243328282313593461300703078854044587993248807613713896590402657788194264718603549894361488507629356532718775278399264279359256975688280723740017979438505001819438,
33322989148902718763644384246610630825314206644879155585369541624158380990667828419255828083639294898100922608833810585530801931417726134558845725168047585271855248605561256531342703212030641555260907310067120102069499927711242804407691706542428236208695153618955781372741765233319988193384708525251620506966304554054884590718068210659709406626033891748214407992041364462525367373648910810036622684929049996166651416565651803952838857960054689875755131784246099270581394)
e = 0x10001
def continuedFra(x, y): #不断生成连分数的项,如127/52 = 2 + 1/2+ 1/3+ 1/1+ 1/5,生成[2,2,3,1,5]
cF = []
while y:
cF += [x // y]
x, y = y, x % y #这里是连分数生成项的算法
return cF
def Simplify(ctnf): #对前面生成的连分数项化简
numerator = 1
denominator = 0
for x in ctnf[::-1]: #注意这里是倒叙遍历,从后面把连分数项合成总和。
numerator, denominator = x * numerator + denominator, numerator
return (numerator, denominator) #把连分数分成和算出来的分母以元组的形式导出来,如Simplify(continuedFra(127,52))生成(127, 52)
def getit(c):
cf=[]
for i in range(1,len(c)):
cf.append(Simplify(c[:i])) #各个阶段的连分数的分子和分母
return cf #得到一串连分数,如:[(2, 1), (5, 2), (17, 7), (22, 9)]
def wienerAttack(e, n): #低解密指数攻击,自己修改要碰撞的分子或分母
cf=continuedFra(e,n)
for (q1,q2) in getit(cf): #遍历得到的连分数,令分子分母分别是Q1,Q2,因为前面我们说了N1/N2=(p1/p2)**2 (q1/q2)
if q1 == 0:
continue
if N1%q1==0 and q1!=1: #满足这个条件就找到了Q1,其实同理也可以找出Q2
return (q1,q2)
print('没找到能覆盖的分子/分母')
q1,q2=wienerAttack(N1,N2) #找出一个Q1,其实这里也可以找出Q2的,但处于p2=sympy.nextprime(p1)的限制,只能用p1求出p2,不能用p2求出p1
from Crypto.Util.number import *
p1=gmpy2.iroot(N1//q1,4)[0]
p2=gmpy2.iroot(N2//q2,4)[0]
phi1=((p1-1)*p1*p1*p1)*(q1-1)
phi2=((p2-1)*p2*p2*p2)*(q2-1)
d1=gmpy2.invert(e,phi1) #逆模求d
d2=gmpy2.invert(e,phi2) #逆模求d
m1=long_to_bytes(gmpy2.powmod(c1,d1,N1)) #普通解密算法,但是要用将long型数转为字节型数据
m2=long_to_bytes(gmpy2.powmod(c2,d2,N2)) #普通解密算法,但是要用将long型数转为字节型数据
print((m1+m2)) #拼接flag字符。
结果:
.
.
解毕!敬礼!