crypto_whenthedayhascome2022(rsa求解中的e和phi不互素的案例)

今年遇到的一道密码学线上赛题,感觉挺有意思大概记录一下。

题目大概是给出rsa密文:

c=23129683905221590810931073733257240695491909600600821626110967741991475952367362466435001712032183901737453265086024660692796844392297498997208235843621067335947024294793917437465567235097523891328309930140339168673282959013006525102332444767839426566939309039615858490944730603509080574471734733118066791730903874584695936232044353090855494398794086718463881637739165577843547326511641921822024263267673375341299485422153934060527418515955523844699687688348603999820720218160844840953001023840468960215702467203343095303314120059019184362079713388502293450806476408625709403546751299379519145557188933345613844133126

然后提供数百个公钥文件,其中有一个修改时间较为特殊key.txt:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt0TBlWiuPttokM+14UTq
dxwQ/oJO2lqsQ7yuGzXj6twLpqHEmOSlGRvpHVlPYvRKxQeILpfwim829y2dwnMK
Ri+V4dIM3kn7lfxWq3k1xaFuj3VTwpSUeWFzzNeRdodooI0Ign6tuafmVC8GU0ZB
5j8io0XuPyzmHgrw8ZGKG8A/24cL4Zh0hboB/NOlWXEgGueKGB4t6GkdQGB9mZGc
sJTeZdHMiFyyX6n3yWApIkRl/rvWChpvj3XXdaFc7zewApS1juOmMYSirb0g0QO9
Nba//dzVjkDHr7f0CwlJQRxGSTczLaznFDlCDi+CZ/k7tpWOCEuSbCQNe4f0RFYU
fwIDF1+R
-----END PUBLIC KEY-----

第一反应先取n和e:

openssl rsa -pubin -text -modulus -in key.txt

结果为:

Modulus:
    00:b7:44:c1:95:68:ae:3e:db:68:90:cf:b5:e1:44:
    ea:77:1c:10:fe:82:4e:da:5a:ac:43:bc:ae:1b:35:
    e3:ea:dc:0b:a6:a1:c4:98:e4:a5:19:1b:e9:1d:59:
    4f:62:f4:4a:c5:07:88:2e:97:f0:8a:6f:36:f7:2d:
    9d:c2:73:0a:46:2f:95:e1:d2:0c:de:49:fb:95:fc:
    56:ab:79:35:c5:a1:6e:8f:75:53:c2:94:94:79:61:
    73:cc:d7:91:76:87:68:a0:8d:08:82:7e:ad:b9:a7:
    e6:54:2f:06:53:46:41:e6:3f:22:a3:45:ee:3f:2c:
    e6:1e:0a:f0:f1:91:8a:1b:c0:3f:db:87:0b:e1:98:
    74:85:ba:01:fc:d3:a5:59:71:20:1a:e7:8a:18:1e:
    2d:e8:69:1d:40:60:7d:99:91:9c:b0:94:de:65:d1:
    cc:88:5c:b2:5f:a9:f7:c9:60:29:22:44:65:fe:bb:
    d6:0a:1a:6f:8f:75:d7:75:a1:5c:ef:37:b0:02:94:
    b5:8e:e3:a6:31:84:a2:ad:bd:20:d1:03:bd:35:b6:
    bf:fd:dc:d5:8e:40:c7:af:b7:f4:0b:09:49:41:1c:
    46:49:37:33:2d:ac:e7:14:39:42:0e:2f:82:67:f9:
    3b:b6:95:8e:08:4b:92:6c:24:0d:7b:87:f4:44:56:
    14:7f
Exponent: 1531793 (0x175f91)
Modulus=B744C19568AE3EDB6890CFB5E144EA771C10FE824EDA5AAC43BCAE1B35E3EADC0BA6A1C498E4A5191BE91D594F62F44AC507882E97F08A6F36F72D9DC2730A462F95E1D20CDE49FB95FC56AB7935C5A16E8F7553C29494796173CCD791768768A08D08827EADB9A7E6542F06534641E63F22A345EE3F2CE61E0AF0F1918A1BC03FDB870BE1987485BA01FCD3A55971201AE78A181E2DE8691D40607D99919CB094DE65D1CC885CB25FA9F7C96029224465FEBBD60A1A6F8F75D775A15CEF37B00294B58EE3A63184A2ADBD20D103BD35B6BFFDDCD58E40C7AFB7F40B0949411C464937332DACE71439420E2F8267F93BB6958E084B926C240D7B87F44456147F

固有套路分解一下n得到p和q:

q = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509191077550351496973264159350455849525747355370985161471258126994336297660442739951587911017897809328177973473427538782352524239389465259173507406981248869793
p = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509185464641520736454955410019736330026303289754303711165526821866422766844554206047678337249535003432035470125187072461808523973483360158652600992259609986591

本来打算套一下脚本就齐活了:

import gmpy2
import rsa
p = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509191077550351496973264159350455849525747355370985161471258126994336297660442739951587911017897809328177973473427538782352524239389465259173507406981248869793
q = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509185464641520736454955410019736330026303289754303711165526821866422766844554206047678337249535003432035470125187072461808523973483360158652600992259609986591
n = 23135514747783882716888676812295359006102435689848260501709475114767217528965364658403027664227615593085036290166289063788272776788638764660757735264077730982726873368488789034079040049824603517615442321955626164064763328102556475952363475005967968681746619179641519183612638784244197749344305359692751832455587854243160406582696594311842565272623730709252650625846680194953309748453515876633303858147298846454105907265186127420148343526253775550105897136275826705375222242565865228645214598819541187583028360400160631947584202826991980657718853446368090891391744347723951620641492388205471242788631833531394634945663
e = 1531793
print(gmpy2.gcd(e, (p-1)*(q-1)))
d = int(gmpy2.invert(e,(p-1)*(q-1)))
privatekey = rsa.PrivateKey(n,e,d,p,q)
with open("flag.txt","rb") as f:
    print(rsa.decrypt(f.read(),privatekey).decode())

没想到终端打出了一句:

Traceback (most recent call last):
  File "when.py", line 11, in <module>
    d = int(gmpy2.invert(e,(p-1)*(q-1)))
ZeroDivisionError: invert() no inverse exists

王德发?这啥呀?研究了一下这个报错大多是因为invert的两个参数不互素导致的,顺藤摸瓜找到了这篇文章:奇安信攻防社区-有限域上的高次开根AMM算法在RSA上的应用

有限域上的高次开根AMM算法在RSA上的应用

个人理解就是当e和phi不互素时rsa私钥无唯一解,需要利用中国剩余定理进行遍历求解。但以上文档里的例题是e与p-1和q-1均不互素。而本题仅仅是e与q-1不互素,抱着试一试的态度进行了一番定制化:

import gmpy2
c = 23129683905221590810931073733257240695491909600600821626110967741991475952367362466435001712032183901737453265086024660692796844392297498997208235843621067335947024294793917437465567235097523891328309930140339168673282959013006525102332444767839426566939309039615858490944730603509080574471734733118066791730903874584695936232044353090855494398794086718463881637739165577843547326511641921822024263267673375341299485422153934060527418515955523844699687688348603999820720218160844840953001023840468960215702467203343095303314120059019184362079713388502293450806476408625709403546751299379519145557188933345613844133126
q = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509191077550351496973264159350455849525747355370985161471258126994336297660442739951587911017897809328177973473427538782352524239389465259173507406981248869793
p = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509185464641520736454955410019736330026303289754303711165526821866422766844554206047678337249535003432035470125187072461808523973483360158652600992259609986591
e = 0x175f91

c1 = c%p
d = gmpy2.invert(e,p-1)
c1 = pow(c1,d,p)
print(c1)
#求出m%p的解
#mmp=56006392793407639221973721494662966801839289211190365946850804877854678967261676894752749225997054589

然后套入AMM求解脚本,由于本题e数值较大,这段代码放进sage里面跑了将近6个小时:

import random
import time
import binascii

Parallelism().set('tensor', nproc=4)

# About 3 seconds to run
def AMM(o, r, q):
    start = time.time()
    print('\n----------------------------------------------------------------------------------')
    print('Start to run Adleman-Manders-Miller Root Extraction Method')
    print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
    g = GF(q)
    o = g(o)
    p = g(random.randint(1, q))
    while p ^ ((q-1) // r) == 1:
        p = g(random.randint(1, q))
    print('[+] Find p:{}'.format(p))
    t = 0
    s = q - 1
    while s % r == 0:
        t += 1
        s = s // r
    print('[+] Find s:{}, t:{}'.format(s, t))
    k = 1
    while (k * s + 1) % r != 0:
        k += 1
    alp = (k * s + 1) // r
    print('[+] Find alp:{}'.format(alp))
    a = p ^ (r**(t-1) * s)
    b = o ^ (r*alp - 1)
    c = p ^ s
    h = 1
    for i in range(1, t):
        d = b ^ (r^(t-1-i))
        if d == 1:
            j = 0
        else:
            print('[+] Calculating DLP...')
            j = - discrete_log(d, a)
            print('[+] Finish DLP...')
        b = b * (c^r)^j
        h = h * c^j
        c = c^r
    result = o^alp * h
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    print('Find one solution: {}'.format(result))
    return result

def findAllPRoot(p, e):
    print("Start to find all the Primitive {:#x}th root of 1 modulo {}.".format(e, p))
    start = time.time()
    proot = set()
    while len(proot) < e:
        proot.add(pow(random.randint(2, p-1), (p-1)//e, p))
        #print(len(proot))
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return proot

def findAllSolutions(mp, proot, cp, p):
    print("Start to find all the {:#x}th root of {} modulo {}.".format(e, cp, p))
    start = time.time()
    all_mp = set()
    for root in proot:
        mp2 = mp * root % p
        assert(pow(mp2, e, p) == cp)
        all_mp.add(mp2)
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return all_mp

c = 23129683905221590810931073733257240695491909600600821626110967741991475952367362466435001712032183901737453265086024660692796844392297498997208235843621067335947024294793917437465567235097523891328309930140339168673282959013006525102332444767839426566939309039615858490944730603509080574471734733118066791730903874584695936232044353090855494398794086718463881637739165577843547326511641921822024263267673375341299485422153934060527418515955523844699687688348603999820720218160844840953001023840468960215702467203343095303314120059019184362079713388502293450806476408625709403546751299379519145557188933345613844133126
q = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509191077550351496973264159350455849525747355370985161471258126994336297660442739951587911017897809328177973473427538782352524239389465259173507406981248869793
p = 152103631606164757991388657189704366976433537820099034648874538500153362765519668135545276650144504533686483692163171569868971464706026329525740394016509185464641520736454955410019736330026303289754303711165526821866422766844554206047678337249535003432035470125187072461808523973483360158652600992259609986591
e = 1531793

#cp = c % p
cq = c % q
#mp = AMM(cp, e, p)
mq = AMM(cq, e, q)
#p_proot = findAllPRoot(p, e)
q_proot = findAllPRoot(q, e)
#mps = findAllSolutions(mp, p_proot, cp, p)
mps = [56006392793407639221973721494662966801839289211190365946850804877854678967261676894752749225997054589]
mqs = findAllSolutions(mq, q_proot, cq, q)
print(mps, '---',mqs)

def check(m):
    h = m.hex()
    if len(h) & 1:
        return False
    s=binascii.unhexlify(h)
    #print('checking...',s)
    if s.startswith(b'flag'):
        print(s)
        return True
    else:
        return False

# About 16 mins to run 0x1337^2 == 24196561 times CRT
start = time.time()
print('Start CRT...')
for mpp in mps:
    for mqq in mqs:
        solution = CRT_list([int(mpp), int(mqq)], [p, q])
        if check(solution):
            print(solution)
    print(time.time() - start)

end = time.time()
print("Finished in {} seconds.".format(end - start))

最后确实能得到flag,但从时间复杂度上来看隐约觉得应该有更简便的做法,希望路过各位师傅能予以斧正。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值