[羊城杯 2020]RRRRRRRSA 题解(wiener attack运用)

简介

自己写脚本搞定这道题有助于理解wiener attack,在多种关于RSA攻击方法中,这种方法算是比较难理解的一种,本题设计还是比较有意思的,算是该攻击方式的一个灵活运用,单纯依靠现成脚本也是不行的,必须学会wiener attack的原理才能顺利解决该问题。

原理阐述

wiener attack 是依靠连分数进行的攻击方式,适用于非常接近某一值(比如1)时,求一个比例关系,通过该比例关系再来反推关键信息就简单很多。这种攻击对于解密指数d很小时有很好的效果,一般的用法是通过
ed mod phi(N)=1

得到 ed=k*phi(N)+1

即 e/phi(N)=k/d+1/phi(N)
这种情况下phi(N)≈N,且phi(N)非常大
所以有 e/N - k/d = 1/phi(N),也就是说k/d与e/N非常接近,而e/N又是已知的
对e/N进行连分数展开,得到的一串分数的分母很有可能就是d(论文太难了,只能记结论了T_T),只要检验一下

ed mod phi(N)

看它是不是1就知道对不对了。

但是这道题倒是告诉我,wiener attack的利用并不一定是这样的方式,只要其他值符合类似的条件,也可以试着求出一个关键值。

另外注意:wiener attack使用是要满足一定数值条件的,对d和N的相对大小,以及p,q的相对大小都是有要求的,用之前可以先大概计算一下是否合适。(LaTaX公式编辑器不会用呀。。就不想把具体公式再打上来了)

题解

分析

先看看题干,明显可以发现加密指数e非常大,有经验就晓得这和wiener attack脱不了干系。
代码的逻辑也很好理解,先是把flag分成两部分(其实两部分加密方式是一样的),然后生成两个非常接近的大素数P1,P2,然后生成了两个相邻的较小的大素数,用P1的平方和Q1相乘得到N1,N2同理。用两个相近巨大的加密指数E加密。

import hashlib
import sympy
from Crypto.Util.number import *

flag = 'GWHT{************}'

flag1 = flag[:19].encode()
flag2 = flag[19:].encode()
assert(len(flag) == 38)

P1 = getPrime(1038)
P2 = sympy.nextprime(P1)
assert(P2 - P1 < 1000)

Q1 = getPrime(512)
Q2 = sympy.nextprime(Q1)

N1 = P1 * P1 * Q1
N2 = P2 * P2 * Q2

E1 = getPrime(1024)
E2 = sympy.nextprime(E1)

m1 = bytes_to_long(flag1)
m2 = bytes_to_long(flag2)

c1 = pow(m1, E1, N1)
c2 = pow(m2, E2, N2)


output = open('secret', 'w')
output.write('N1=' + str(N1) + '\n')
output.write('c1=' + str(c1) + '\n')
output.write('E1=' + str(E1) + '\n')
output.write('N2=' + str(N2) + '\n')
output.write('c2=' + str(c2) + '\n')
output.write('E2=' + str(E2) + '\n')
output.close()
'''
N1=60143104944034567859993561862949071559877219267755259679749062284763163484947626697494729046430386559610613113754453726683312513915610558734802079868190554644983911078936369464590301246394586190666760362763580192139772729890492729488892169933099057105842090125200369295070365451134781912223048179092058016446222199742919885472867511334714233086339832790286482634562102936600597781342756061479024744312357407750731307860842457299116947352106025529309727703385914891200109853084742321655388368371397596144557614128458065859276522963419738435137978069417053712567764148183279165963454266011754149684758060746773409666706463583389316772088889398359242197165140562147489286818190852679930372669254697353483887004105934649944725189954685412228899457155711301864163839538810653626724347
c1=55094296873556883585060020895253176070835143350249581136609315815308788255684072804968957510292559743192424646169207794748893753882418256401223641287546922358162629295622258913168323493447075410872354874300793298956869374606043622559405978242734950156459436487837698668489891733875650048466360950142617732135781244969524095348835624828008115829566644654403962285001724209210887446203934276651265377137788183939798543755386888532680013170540716736656670269251318800501517579803401154996881233025210176293554542024052540093890387437964747460765498713092018160196637928204190194154199389276666685436565665236397481709703644555328705818892269499380797044554054118656321389474821224725533693520856047736578402581854165941599254178019515615183102894716647680969742744705218868455450832
E1=125932919717342481428108392434488550259190856475011752106073050593074410065655587870702051419898088541590032209854048032649625269856337901048406066968337289491951404384300466543616578679539808215698754491076340386697518948419895268049696498272031094236309803803729823608854215226233796069683774155739820423103
N2=60143104944034567859993561862949071559877219267755259679749062284763163484947626697494729046430386559610613113754453726683312513915610558734802079868195633647431732875392121458684331843306730889424418620069322578265236351407591029338519809538995249896905137642342435659572917714183543305243715664380787797562011006398730320980994747939791561885622949912698246701769321430325902912003041678774440704056597862093530981040696872522868921139041247362592257285423948870944137019745161211585845927019259709501237550818918272189606436413992759328318871765171844153527424347985462767028135376552302463861324408178183842139330244906606776359050482977256728910278687996106152971028878653123533559760167711270265171441623056873903669918694259043580017081671349232051870716493557434517579121
c2=39328446140156257571484184713861319722905864197556720730852773059147902283123252767651430278357950872626778348596897711320942449693270603776870301102881405303651558719085454281142395652056217241751656631812580544180434349840236919765433122389116860827593711593732385562328255759509355298662361508611531972386995239908513273236239858854586845849686865360780290350287139092143587037396801704351692736985955152935601987758859759421886670907735120137698039900161327397951758852875291442188850946273771733011504922325622240838288097946309825051094566685479503461938502373520983684296658971700922069426788236476575236189040102848418547634290214175167767431475003216056701094275899211419979340802711684989710130215926526387138538819531199810841475218142606691152928236362534181622201347
E2=125932919717342481428108392434488550259190856475011752106073050593074410065655587870702051419898088541590032209854048032649625269856337901048406066968337289491951404384300466543616578679539808215698754491076340386697518948419895268049696498272031094236309803803729823608854215226233796069683774155739820425393
'''

但是和普通的wiener attack 不同的是,e与N并没有近到相除约为1的地步,相差还是很大的,也就是说解密指数d也许还是很大的,这样就解不出来。
这道题有意思的部分就在于,e和N的关系不符合利用条件,但是N1和N2的关系却适合
对于这一道题:
N1/N2=(P1/P2)**2 * (Q1/Q2)
显然我们可以知道的是N1/N2 所以在Q1/Q2在区间(N1/N2,1)之间 (这是关键)
尝试对N1/N2进行连分数展开并求其各项渐进分数,其中某个连分数的分母可能就是Q1(这个可以依靠N%Q来验证)

算法实现

连分数生成

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 getit(c):
    cf=[]
    for i in range(1,len(c)):
        cf.append(Simplify(c[:i])) #各个阶段的连分数的分子和分母
    return cf #得到一串连分数

寻找合适的Q1

def wienerAttack(e, n):
    cf=continuedFra(e,n)
    for (Q2,Q1) in getit(cf):#遍历得到的连分数,令分子分母分别是Q2,Q1
        if Q1 == 0:
            continue
        if N1%Q1==0 and Q1!=1:#满足这个条件就找到了
            return Q1
    print('not find!')
Q1=wienerAttack(N1,N2)

最后的解密

找到Q1后顺势就可以求出所有其他参数

from Crypto.Util.number import *
P1=gmpy2.iroot(N1//Q1,2)[0]
P2=gmpy2.next_prime(P1)
Q2=gmpy2.next_prime(Q1)
phi1=P1*(P1-1)*(Q1-1)
phi2=P2*(P2-1)*(Q2-1)
d1=gmpy2.invert(E1,phi1)
d2=gmpy2.invert(E2,phi2)
m1=long_to_bytes(gmpy2.powmod(c1,d1,N1))
m2=long_to_bytes(gmpy2.powmod(c2,d2,N2))
print((m1+m2))

总结

这题中主要要学的就是wiener attack的原理,其实wiener attack并不是针对于解密指数的攻击方式,实际上,再低解密指数的情况下,任何比例非常接近另外一个已知比例的情况下都可以尝试用这个方法得到RSA中重要解密信息,不一定是d,也有可能是p或者q。

参考:羊城杯 Crypto RRRRRRRSA (连分数,低解密指数攻击原理)

你可能感兴趣的:(Crypto)