跳转至

RSA入门

原文链接

RSA学习过程,对常见题型进行了整合

发表于2023年7月6日。于2024年7月15日进行更新,把以前三篇有关RSA的内容整合成一篇,添加了一些题目,并且去除了冗余的内容

一、RSA算法原理简介

1.密钥的产生

  1. 选择两个保密的大素数\(p\)\(q\)

  2. 计算\(n = p \times q,\phi(n) = (p-1)(q-1)\),其中\(\phi(n)\)是n的欧拉函数值

  3. 选一整数\(e\),满足\(1 < e < \phi(n)\),且\(gcd(\phi(n),e) = 1\)

  4. 计算\(d\),满足\(ed \equiv 1 \mod \phi(n)\)。即\(d\)是e在模\(\phi(n)\)下的乘法逆元,因为\(e\)\(\phi(n)\)互素,所以\(d\)必定存在

  5. \((e,n)\)为公开钥,\((d,n)\)为秘密钥

2.加密

加密时,先把明文比特串分组,使得每个分组对应的十进制数\(<n\),即分组长度小于\(log_2n\)。如然后对每个明文分组\(m\),作加密运算:

\[ c \equiv m^e \mod n \]

3.解密

对密文分组的解密运算:

\[ m \equiv c ^d \mod n \]

4.证明解密的正确性

情况1:\(m,n\)互素。由欧拉定理:

\[ c^d \equiv m^{ed} \equiv m^{k\phi(n)+1} \equiv m\times (m^{\phi(n)})^k \equiv m \mod n \]

\(c^d \equiv m \mod n\)

情况2:\(m,n\)不互素。

因为\(m,n\)不互素,注意到\(n = pq\),所以此时\(m\)\(p\)或者\(q\)的倍数。假设\(m\)\(p\)的倍数,即\(m = tp\)

此时\(gcd(m,q) = 1\),否则\(m\)也是\(q\)的倍数,从而\(m\)是n的倍数,这与明文分组\(m < n\)矛盾。所以不可能

\(gcd(m,q) = 1\)

\[ m ^{\phi(q)} \equiv 1 \mod q \]

所以

\[ m^{k\phi(p)\phi(q)} \equiv (m^{\phi(q)})^{k\phi(p)} \equiv 1\mod q \]

\(m^{k\phi(n)} \equiv 1 \mod q\),也就是\(m^{k\phi(n)} = kq + 1\)

两边同时乘上\(m = tp\),得\(m^{k\phi(n)+1} \equiv ktpq + m = m+tkn\)

也就是\(m^{k\phi(n)+1} \equiv m \mod n\)

二、基础工具

1.yafu

使用方法:进入yafu所在的文件夹,输入命令yafu-x64 factor(n)

2.网站分解

http://www.factordb.com/index.php

三、密钥生成和读取

密钥生成

通过Crypto.PublicKey.RSA中的construct函数可以生成公钥和私钥。

对于生成公钥,需要两个参数\(n,e\),生成私钥需要\(n,e,d\)

需要配合exprot_Key使用

生成公钥

Python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA

p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537

a = (n,e)
publickey = RSA.construct(a)
with open("publickey.pem","wb") as f:
    f.write(publickey.export_key())

生成私钥

必需参数是\(n,e,d\),可添参数是\(p,q\)

Python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import gmpy2

p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))

a = (n,e)
b = (n,e,int(d))

publickey = RSA.construct(a)
privatekey = RSA.construct(b)

with open("publickey.pem","wb") as f:
    f.write(publickey.export_key())

with open("privatekey.pem","wb") as f2:
    f2.write(privatekey.export_key())

读取密钥

Python
from Crypto.PublicKey import RSA

key = RSA.import_Key(文件)

key中有p,q,n,e,d五个属性,一般题目也就给了公钥,所以只有n,e是能读取的

例题1 泄露私钥

task

Python
from Crypto.PublicKey import RSA
import libnum
import gmpy2
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)

p = libnum.generate_prime(1024)
q = gmpy2.next_prime(p)
n = p * q
e = 65537
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = libnum.s2n(flag)
c = pow(m, e, n)
#
c1 = libnum.n2s(int(c))

with open("flag.pem", "wb") as f:
    f.write(c1)
## 生成公钥
rsa_components = (int(n), int(e), int(d))
keypair = RSA.construct(rsa_components)
with open('pubckey.pem', 'wb') as f:
    f.write(keypair.exportKey())

题目中把d也传入数据了,直接读取d解密即可

Python
from Crypto.PublicKey import RSA
from Crypto.Util.number import *

c = bytes_to_long(open("flag.pem","rb").read())
key = RSA.import_key(open("publickey.pem","rb").read())
e = key.e
n = key.n
d = key.d

print(long_to_bytes(pow(c,d,n)))

## flag{947ce8a3-40ee-46c0-a00e-0026e583f8da}

例题2 OAEP

task.py

Python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import libnum
import gmpy2
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
flag = flag.encode()

p = libnum.generate_prime(1024)
q = gmpy2.next_prime(p)
n = p * q
e = 65537
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)

#
## 生成公钥
rsa_components = (int(n), int(e), int(d))
keypair = RSA.construct(rsa_components)
with open('prikey2.pem', 'wb') as f:
    f.write(keypair.exportKey())

rsa_components = (int(n), e,)
arsa = RSA.construct(rsa_components)
rsakey = RSA.importKey(arsa.exportKey())
rsakey = PKCS1_OAEP.new(rsakey)
c = rsakey.encrypt(flag)
with open("flag2.pem", "wb") as f:
    f.write(c)

了解了一下函数

Python
PKCS1_OAEP.new(key, hashAlgo=None, mgfunc=None, label=b'')

参数含义如下:

key:RSA密钥,可以是公钥或私钥;

hashAlgo:哈希算法,默认为SHA-1;

myfunc:消息生成函数,默认为MGF1;

label:可选的附加数据,默认为空。

key是必需参数

使用PKCS1_OAEP.new初始化后,可以调用其实例方法encryptdecrypt实现RSA-OAEP算法的加密和解密操作

exp.py

Python
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey.RSA import *

c = open("flag2.pem","rb").read()
key = import_key(open("prikey2.pem","rb").read())

newkey = PKCS1_OAEP.new(key)
m = newkey.decrypt(c)
print(m).

#flag{04bbaad0-9241-42a4-9fd4-7d6e0d8bc5f1}

攻防世界——cr4-poor-rsa

题目给了个无后缀的文件,拖到010发现里面有藏两个文件,于是把后缀改成了zip

打开后得到两个文件,分别是key.pub和flag.b64

key.pub

Text Only
-----BEGIN PUBLIC KEY-----
ME0wDQYJKoZIhvcNAQEBBQADPAAwOQIyUqmeJJ7nzzwMv5Y6AJZhdyvJzfbh4/v8
bkSgel4PiURXqfgcOuEyrFaD01soulwyQkMCAwEAAQ==
-----END PUBLIC KEY-----

flag.b64

Text Only
Ni45iH4UnXSttNuf0Oy80+G5J7tm8sBJuDNN7qfTIdEKJow4siF2cpSbP/qIWDjSi+w=

先试着读取公钥,发现n可以分解,于是就按常规流程做了

exp.py

Python
import base64
from Crypto.PublicKey.RSA import *
from Crypto.Util.number import *
import gmpy2

c = base64.b64decode(open("flag.b64","rb").read())
c = bytes_to_long(c)
key = import_key(open("key.pub","rb").read())

p = 863653476616376575308866344984576466644942572246900013156919
q = 965445304326998194798282228842484732438457170595999523426901
n = p*q
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)))

#XCTF{SMALL_PRIMES_ARE_BAD}

四、共模攻击

共模攻击:

在对同一明文的加密过程中使用相同的模数n,对同一明文,用不同的密钥e(两个密钥是互素的)进行加密

结果是得到两个不同的c

原理:

\[ \begin{align} c_1 \equiv m^{e_1} \mod n\\\\ c_2 \equiv m^{e_2} \mod n \end{align} \]

通过欧几里得扩展算法我们能得到\(x,y\)满足\(e_1x+e_2y = 1\)

\[ \therefore c_1^x \times c_2^y \equiv m^{e_1x + e_2y} \equiv m \mod n \]

例题1

task.py

Python
import libnum
import gmpy2
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n1 = p * q
n2 = p * q
e1 = 2333
e2 = 23333
m = libnum.s2n(flag)
c1 = pow(m, e1, n1)
c2 = pow(m, e2, n2)
print("n1=", n1)
print("n2=", n2)
print("e1=", e1)
print("e2=", e2)
print("c1=", c1)
print("c2=", c2)

#n1 = 12023886737570921683430494088148056717464277480371493354080633886982376602419433228186314817561301719123238737516332784081267153425832030515178119047675516911098595227477026283152544604891747727831780305507300318674027062554009254728767714650522432836286987070040177863862115871377017779058128916872854380528430193235920536818893053943407063308419618772087299760070707222914961338101044775521373972076936552277418325268112523349134412986872504187930360266568935217397303420305220796347316727211659529079762169876950534044014924448371804442314283893083178368082712851107281302456671010073505430574108861981588149293779
n2 = 12023886737570921683430494088148056717464277480371493354080633886982376602419433228186314817561301719123238737516332784081267153425832030515178119047675516911098595227477026283152544604891747727831780305507300318674027062554009254728767714650522432836286987070040177863862115871377017779058128916872854380528430193235920536818893053943407063308419618772087299760070707222914961338101044775521373972076936552277418325268112523349134412986872504187930360266568935217397303420305220796347316727211659529079762169876950534044014924448371804442314283893083178368082712851107281302456671010073505430574108861981588149293779
e1 = 2333
e2 = 23333
c1 = 1316116662134770690879814362103839780623420120527248536043840592146479052480574077985474161623763978563721124073172820410730492348846200098142706235343164470686127445583938273863894304189618247054649514955176136464273395879832878841555224421879457659795562326746943199675846414637238040550327393009642569894024250271081839428945999237716296592560124669418322569188493036148885333003876760965512925618500360394774816066758106739359762817644284120811162065280330204951295150904138010974815308787047834776406610525102814356091515999954110712767658162496023213125548829820563945272374105274832862682574678195529192009516
c2 =6485241395763328009719746130709898541269729483150505308808259329749145687803066648274311801821624527910483266170666538736992203392620205417714840881369386852010836477498279266591695876758050686740322941452286584178315830797555697887040771666991377055060541491757349967338300117181859105577325308779010792879713808168285776399372981366988860647334022480774711504685240194804912592209253106123423232743785805952113875347267336118332317990496240807273787216894980604742600774512296661048914646776553393778079057461747246478299158814839681875752645552215714984659603917168300453505504140987829883479097467840565806608012

m = pow(c1,x,n) * pow(c2,y,n) % n

exp.py

Python
from Crypto.Util.number import *
import gmpy2

n = 12023886737570921683430494088148056717464277480371493354080633886982376602419433228186314817561301719123238737516332784081267153425832030515178119047675516911098595227477026283152544604891747727831780305507300318674027062554009254728767714650522432836286987070040177863862115871377017779058128916872854380528430193235920536818893053943407063308419618772087299760070707222914961338101044775521373972076936552277418325268112523349134412986872504187930360266568935217397303420305220796347316727211659529079762169876950534044014924448371804442314283893083178368082712851107281302456671010073505430574108861981588149293779
e1 = 2333
e2 = 23333
c1 =1316116662134770690879814362103839780623420120527248536043840592146479052480574077985474161623763978563721124073172820410730492348846200098142706235343164470686127445583938273863894304189618247054649514955176136464273395879832878841555224421879457659795562326746943199675846414637238040550327393009642569894024250271081839428945999237716296592560124669418322569188493036148885333003876760965512925618500360394774816066758106739359762817644284120811162065280330204951295150904138010974815308787047834776406610525102814356091515999954110712767658162496023213125548829820563945272374105274832862682574678195529192009516
c2 =6485241395763328009719746130709898541269729483150505308808259329749145687803066648274311801821624527910483266170666538736992203392620205417714840881369386852010836477498279266591695876758050686740322941452286584178315830797555697887040771666991377055060541491757349967338300117181859105577325308779010792879713808168285776399372981366988860647334022480774711504685240194804912592209253106123423232743785805952113875347267336118332317990496240807273787216894980604742600774512296661048914646776553393778079057461747246478299158814839681875752645552215714984659603917168300453505504140987829883479097467840565806608012

s,x,y = gmpy2.gcdext(e1,e2)
m = (pow(c1,x,n)*pow(c2,y,n))%n
print(long_to_bytes(m))

#flag{01d80670b01b654fe4831a3e81870734}

gcdext(e1,e2)返回3个元素

第一个元素是\(Boolean\)类型的数据,判断存不存在\(x,y\)使得\(e_1x+e_2y = 1\)

返回得第二个和第三个元素是\(x,y\)

例题2

这个例题的情况是\(gcd(e_1,e_2)\ne 1\)

task.py

Python
from Crypto.Util.number import *
from random import getrandbits
import gmpy2
import uuid

while 1:
    flag = "flag{" + str(uuid.uuid4()) + "}"
    m = bytes_to_long(flag.encode())
    p = getPrime(1024)
    q = getPrime(1024)
    n = p * q
    e1 = getrandbits(10)
    e2 = getrandbits(10)
    if e1 != e2 and gmpy2.gcd(e1,e2) != 1:
        break

c1 = pow(m, e1, n)
c2 = pow(m, e2, n)
print("e1 =", e1)
print("e2 =", e2)
print("n =", n)
print("c1 =", c1)
print("c2 =", c2)

这种题目我们还是正常推导,设\(t = gcd(e_1,e_2)\),通过扩展欧几里得算法,有

\[ e_1x + e_2y = t \]

\[ c_1^x \times c_2^y \equiv m^{e_1x+e_2y} \equiv m^t \mod n \]

根据t的大小再进行不同的处理,比如t=3,那么可以试着用低加密指数的思路进行处理。

如果t比较大,那么可以再次当作一次RSA进行求解。

本例t = 2,直接开根就行

exp.py

Python
from Crypto.Util.number import *
import gmpy2

e1 = 578
e2 = 392
n = 21792499489446653209359648268647408248484077502636323240941263512972521642966446318685589383376089358615828479732487823375467576966410911798574546152698546172208644494120844910028661801069139412883722092087841337354582470102991346201250757430966278341015814968426027288609918021263801588485143246793300627932938615206724828284751003817198828431954277335825721403220979778254648036632075276172476930299390290573025768956364488376300510746252434716167769851984446036711610150897081622960442923737196929758209816434803424884970510663599653463189492521652184966337230394292288573277654840483815385709060428243829968709733
c1 = 1295899105546936416711955776176786556954959764395004520666273768220642845167647126115394711085928049897436984534359683209799926877399786065216458011526399070534983898250382591372240231327810785046739203554803467534631081973525005181861203752320250219830020069663762443152692331055158533035896110900630817135766754326408199206651276750278716441636585609321276880631239079781048185864263400051324827820941692502756433463495216541801444097560942509859140724554853380906157874504969878938634453309110942314571026225239704634981956565844426060346340677136929914119932647724346392582368980017754022031748246759437397697793
c2 = 6100251841868768392049795926373721745054594119167283623934416754665831830741776139556785242664295441966913393337547165974512844660057299278828165715333417692799802949938590574117892067491764731548261988101287152727657523152041485072722017224763458684855801781252103126948392647769522681887089175624603484575515518773338807587100984947122699045372679023951413797420560947466351031076552444459222063798664625809884979352261000568340687804017980769948161583294444251776973765636460565633962599948356942127522339715718463747197473475842874431177106692482361060930677857750098337273277996678196019443721319477484374253966

t = gmpy2.gcd(e1,e2)
s,x,y = gmpy2.gcdext(e1,e2)
m = gmpy2.iroot((pow(c1,x,n)*pow(c2,y,n))%n,t)
print(long_to_bytes(m[0]))

五、低加密指数攻击

原理:

加密指数e很小一般是3,导致出现两个情况

第一种情况:\(m^e < n\)\(c \equiv m^{e} \mod n \longrightarrow c = m^e\) 这种情况下对c开e次方就可以得到m的值

第二种情况:\(m^e > n\)(略大一点点),则\(m^e = k*n + c\)这种情况需要爆破k

例题

task.py

Python
import libnum
import gmpy2
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
print(gmpy2.bit_length(m ** 3))
while True:
    p = libnum.generate_prime(504)
    q = libnum.generate_prime(504)
    n = p * q
    phi_n = (p - 1) * (q - 1)
    e = 3
    if gmpy2.gcd(e, phi_n) == 1 and  phi_n%e !=0:
        break

c = pow(m, e, n)
print("n=", n)
print("e=", e)
print("c=", c)

#c= 17567615026662297423639652671128685098763112348521263232850922784902991105809108670614334001294254886850775709702759646022208238160649099765454315761185950859820327234504422026634432251632399712621439201146
3091635444512796333373451321795475333496092463760390199602320265733668648810943598505902205569125
n = 1101676297168703265566511587913652821222614528632844563918598090813090976948138058144049294690727841463413972173051671908835164088465174349647584948964206244648736138253802417241570633968307801570794459668533128958442296813160786428069813867034205462528763830205245218089660432399549540588101288362866463
e = 3

exp.py

Python
from Crypto.Util.number import *
import gmpy2

n = 1101676297168703265566511587913652821222614528632844563918598090813090976948138058144049294690727841463413972173051671908835164088465174349647584948964206244648736138253802417241570633968307801570794459668533128958442296813160786428069813867034205462528763830205245218089660432399549540588101288362866463
e = 3
c = 175676150266622974236396526711286850987631123485212632328509227849029911058091086706143340012942548868507757097027596460222082381606490997654543157611859508598203272345044220266344322516323997126214392011463091635444512796333373451321795475333496092463760390199602320265733668648810943598505902205569125
k = 0

t = gmpy2.iroot(c,e)
if t[1]:
    print(long_to_bytes(t[0]))
else:
    for i in trange() 1:
        m = gmpy2.iroot(k*n+c,e)
        if m[1]:
            print(long_to_bytes(m[0]))
            break
        else:
            k += 1

六、广播攻击

原理:

明文\(m\)用相同的密钥\(e\)和不同的模\(n\)进行加密

一般会得到几组不同的\(n\)\(c\)

第一种情况 多组n之间可能存在公因数

例题 BUUCTF-RSA5

task.txt

Text Only
m = xxxxxxxx
e = 65537
========== n c ==========
n = 20474918894051778533305262345601880928088284471121823754049725354072477155873778848055073843345820697886641086842612486541250183965966001591342031562953561793332341641334302847996108417466360688139866505179689516589305636902137210185624650854906780037204412206309949199080005576922775773722438863762117750429327585792093447423980002401200613302943834212820909269713876683465817369158585822294675056978970612202885426436071950214538262921077409076160417436699836138801162621314845608796870206834704116707763169847387223307828908570944984416973019427529790029089766264949078038669523465243837675263858062854739083634207
c = 974463908243330865728978769213595400782053398596897741316275722596415018912929508637393850919224969271766388710025195039896961956062895570062146947736340342927974992616678893372744261954172873490878805483241196345881721164078651156067119957816422768524442025688079462656755605982104174001635345874022133045402344010045961111720151990412034477755851802769069309069018738541854130183692204758761427121279982002993939745343695671900015296790637464880337375511536424796890996526681200633086841036320395847725935744757993013352804650575068136129295591306569213300156333650910795946800820067494143364885842896291126137320

n = 20918819960648891349438263046954902210959146407860980742165930253781318759285692492511475263234242002509419079545644051755251311392635763412553499744506421566074721268822337321637265942226790343839856182100575539845358877493718334237585821263388181126545189723429262149630651289446553402190531135520836104217160268349688525168375213462570213612845898989694324269410202496871688649978370284661017399056903931840656757330859626183773396574056413017367606446540199973155630466239453637232936904063706551160650295031273385619470740593510267285957905801566362502262757750629162937373721291789527659531499435235261620309759
c = 15819636201971185538694880505120469332582151856714070824521803121848292387556864177196229718923770810072104155432038682511434979353089791861087415144087855679134383396897817458726543883093567600325204596156649305930352575274039425470836355002691145864435755333821133969266951545158052745938252574301327696822347115053614052423028835532509220641378760800693351542633860702225772638930501021571415907348128269681224178300248272689705308911282208685459668200507057183420662959113956077584781737983254788703048275698921427029884282557468334399677849962342196140864403989162117738206246183665814938783122909930082802031855

n = 25033254625906757272369609119214202033162128625171246436639570615263949157363273213121556825878737923265290579551873824374870957467163989542063489416636713654642486717219231225074115269684119428086352535471683359486248203644461465935500517901513233739152882943010177276545128308412934555830087776128355125932914846459470221102007666912211992310538890654396487111705385730502843589727289829692152177134753098649781412247065660637826282055169991824099110916576856188876975621376606634258927784025787142263367152947108720757222446686415627479703666031871635656314282727051189190889008763055811680040315277078928068816491
c = 4185308529416874005831230781014092407198451385955677399668501833902623478395669279404883990725184332709152443372583701076198786635291739356770857286702107156730020004358955622511061410661058982622055199736820808203841446796305284394651714430918690389486920560834672316158146453183789412140939029029324756035358081754426645160033262924330248675216108270980157049705488620263485129480952814764002865280019185127662449318324279383277766416258142275143923532168798413011028271543085249029048997452212503111742302302065401051458066585395360468447460658672952851643547193822775218387853623453638025492389122204507555908862

n = 21206968097314131007183427944486801953583151151443627943113736996776787181111063957960698092696800555044199156765677935373149598221184792286812213294617749834607696302116136745662816658117055427803315230042700695125718401646810484873064775005221089174056824724922160855810527236751389605017579545235876864998419873065217294820244730785120525126565815560229001887622837549118168081685183371092395128598125004730268910276024806808565802081366898904032509920453785997056150497645234925528883879419642189109649009132381586673390027614766605038951015853086721168018787523459264932165046816881682774229243688581614306480751
c = 4521038011044758441891128468467233088493885750850588985708519911154778090597136126150289041893454126674468141393472662337350361712212694867311622970440707727941113263832357173141775855227973742571088974593476302084111770625764222838366277559560887042948859892138551472680654517814916609279748365580610712259856677740518477086531592233107175470068291903607505799432931989663707477017904611426213770238397005743730386080031955694158466558475599751940245039167629126576784024482348452868313417471542956778285567779435940267140679906686531862467627238401003459101637191297209422470388121802536569761414457618258343550613

n = 22822039733049388110936778173014765663663303811791283234361230649775805923902173438553927805407463106104699773994158375704033093471761387799852168337898526980521753614307899669015931387819927421875316304591521901592823814417756447695701045846773508629371397013053684553042185725059996791532391626429712416994990889693732805181947970071429309599614973772736556299404246424791660679253884940021728846906344198854779191951739719342908761330661910477119933428550774242910420952496929605686154799487839923424336353747442153571678064520763149793294360787821751703543288696726923909670396821551053048035619499706391118145067
c = 15406498580761780108625891878008526815145372096234083936681442225155097299264808624358826686906535594853622687379268969468433072388149786607395396424104318820879443743112358706546753935215756078345959375299650718555759698887852318017597503074317356745122514481807843745626429797861463012940172797612589031686718185390345389295851075279278516147076602270178540690147808314172798987497259330037810328523464851895621851859027823681655934104713689539848047163088666896473665500158179046196538210778897730209572708430067658411755959866033531700460551556380993982706171848970460224304996455600503982223448904878212849412357

n = 21574139855341432908474064784318462018475296809327285532337706940126942575349507668289214078026102682252713757703081553093108823214063791518482289846780197329821139507974763780260290309600884920811959842925540583967085670848765317877441480914852329276375776405689784571404635852204097622600656222714808541872252335877037561388406257181715278766652824786376262249274960467193961956690974853679795249158751078422296580367506219719738762159965958877806187461070689071290948181949561254144310776943334859775121650186245846031720507944987838489723127897223416802436021278671237227993686791944711422345000479751187704426369
c = 20366856150710305124583065375297661819795242238376485264951185336996083744604593418983336285185491197426018595031444652123288461491879021096028203694136683203441692987069563513026001861435722117985559909692670907347563594578265880806540396777223906955491026286843168637367593400342814725694366078337030937104035993569672959361347287894143027186846856772983058328919716702982222142848848117768499996617588305301483085428547267337070998767412540225911508196842253134355901263861121500650240296746702967594224401650220168780537141654489215019142122284308116284129004257364769474080721001708734051264841350424152506027932

n = 25360227412666612490102161131174584819240931803196448481224305250583841439581008528535930814167338381983764991296575637231916547647970573758269411168219302370541684789125112505021148506809643081950237623703181025696585998044695691322012183660424636496897073045557400768745943787342548267386564625462143150176113656264450210023925571945961405709276631990731602198104287528528055650050486159837612279600415259486306154947514005408907590083747758953115486124865486720633820559135063440942528031402951958557630833503775112010715604278114325528993771081233535247118481765852273252404963430792898948219539473312462979849137
c = 19892772524651452341027595619482734356243435671592398172680379981502759695784087900669089919987705675899945658648623800090272599154590123082189645021800958076861518397325439521139995652026377132368232502108620033400051346127757698623886142621793423225749240286511666556091787851683978017506983310073524398287279737680091787333547538239920607761080988243639547570818363788673249582783015475682109984715293163137324439862838574460108793714172603672477766831356411304446881998674779501188163600664488032943639694828698984739492200699684462748922883550002652913518229322945040819064133350314536378694523704793396169065179

n = 22726855244632356029159691753451822163331519237547639938779517751496498713174588935566576167329576494790219360727877166074136496129927296296996970048082870488804456564986667129388136556137013346228118981936899510687589585286517151323048293150257036847475424044378109168179412287889340596394755257704938006162677656581509375471102546261355748251869048003600520034656264521931808651038524134185732929570384705918563982065684145766427962502261522481994191989820110575981906998431553107525542001187655703534683231777988419268338249547641335718393312295800044734534761692799403469497954062897856299031257454735945867491191
c = 6040119795175856407541082360023532204614723858688636724822712717572759793960246341800308149739809871234313049629732934797569781053000686185666374833978403290525072598774001731350244744590772795701065129561898116576499984185920661271123665356132719193665474235596884239108030605882777868856122378222681140570519180321286976947154042272622411303981011302586225630859892731724640574658125478287115198406253847367979883768000812605395482952698689604477719478947595442185921480652637868335673233200662100621025061500895729605305665864693122952557361871523165300206070325660353095592778037767395360329231331322823610060006

n = 23297333791443053297363000786835336095252290818461950054542658327484507406594632785712767459958917943095522594228205423428207345128899745800927319147257669773812669542782839237744305180098276578841929496345963997512244219376701787616046235397139381894837435562662591060768476997333538748065294033141610502252325292801816812268934171361934399951548627267791401089703937389012586581080223313060159456238857080740699528666411303029934807011214953984169785844714159627792016926490955282697877141614638806397689306795328344778478692084754216753425842557818899467945102646776342655167655384224860504086083147841252232760941
c = 5418120301208378713115889465579964257871814114515046096090960159737859076829258516920361577853903925954198406843757303687557848302302200229295916902430205737843601806700738234756698575708612424928480440868739120075888681672062206529156566421276611107802917418993625029690627196813830326369874249777619239603300605876865967515719079797115910578653562787899019310139945904958024882417833736304894765433489476234575356755275147256577387022873348906900149634940747104513850154118106991137072643308620284663108283052245750945228995387803432128842152251549292698947407663643895853432650029352092018372834457054271102816934

n = 28873667904715682722987234293493200306976947898711255064125115933666968678742598858722431426218914462903521596341771131695619382266194233561677824357379805303885993804266436810606263022097900266975250431575654686915049693091467864820512767070713267708993899899011156106766178906700336111712803362113039613548672937053397875663144794018087017731949087794894903737682383916173267421403408140967713071026001874733487295007501068871044649170615709891451856792232315526696220161842742664778581287321318748202431466508948902745314372299799561625186955234673012098210919745879882268512656931714326782335211089576897310591491
c = 9919880463786836684987957979091527477471444996392375244075527841865509160181666543016317634963512437510324198702416322841377489417029572388474450075801462996825244657530286107428186354172836716502817609070590929769261932324275353289939302536440310628698349244872064005700644520223727670950787924296004296883032978941200883362653993351638545860207179022472492671256630427228461852668118035317021428675954874947015197745916918197725121122236369382741533983023462255913924692806249387449016629865823316402366017657844166919846683497851842388058283856219900535567427103603869955066193425501385255322097901531402103883869

n = 22324685947539653722499932469409607533065419157347813961958075689047690465266404384199483683908594787312445528159635527833904475801890381455653807265501217328757871352731293000303438205315816792663917579066674842307743845261771032363928568844669895768092515658328756229245837025261744260614860746997931503548788509983868038349720225305730985576293675269073709022350700836510054067641753713212999954307022524495885583361707378513742162566339010134354907863733205921845038918224463903789841881400814074587261720283879760122070901466517118265422863420376921536734845502100251460872499122236686832189549698020737176683019
c = 1491527050203294989882829248560395184804977277747126143103957219164624187528441047837351263580440686474767380464005540264627910126483129930668344095814547592115061057843470131498075060420395111008619027199037019925701236660166563068245683975787762804359520164701691690916482591026138582705558246869496162759780878437137960823000043988227303003876410503121370163303711603359430764539337597866862508451528158285103251810058741879687875218384160282506172706613359477657215420734816049393339593755489218588796607060261897905233453268671411610631047340459487937479511933450369462213795738933019001471803157607791738538467

n = 27646746423759020111007828653264027999257847645666129907789026054594393648800236117046769112762641778865620892443423100189619327585811384883515424918752749559627553637785037359639801125213256163008431942593727931931898199727552768626775618479833029101249692573716030706695702510982283555740851047022672485743432464647772882314215176114732257497240284164016914018689044557218920300262234652840632406067273375269301008409860193180822366735877288205783314326102263756503786736122321348320031950012144905869556204017430593656052867939493633163499580242224763404338807022510136217187779084917996171602737036564991036724299
c = 21991524128957260536043771284854920393105808126700128222125856775506885721971193109361315961129190814674647136464887087893990660894961612838205086401018885457667488911898654270235561980111174603323721280911197488286585269356849579263043456316319476495888696219344219866516861187654180509247881251251278919346267129904739277386289240394384575124331135655943513831009934023397457082184699737734388823763306805326430395849935770213817533387235486307008892410920611669932693018165569417445885810825749609388627231235840912644654685819620931663346297596334834498661789016450371769203650109994771872404185770230172934013971

n = 20545487405816928731738988374475012686827933709789784391855706835136270270933401203019329136937650878386117187776530639342572123237188053978622697282521473917978282830432161153221216194169879669541998840691383025487220850872075436064308499924958517979727954402965612196081404341651517326364041519250125036424822634354268773895465698920883439222996581226358595873993976604699830613932320720554130011671297944433515047180565484495191003887599891289037982010216357831078328159028953222056918189365840711588671093333013117454034313622855082795813122338562446223041211192277089225078324682108033843023903550172891959673551
c = 14227439188191029461250476692790539654619199888487319429114414557975376308688908028140817157205579804059783807641305577385724758530138514972962209062230576107406142402603484375626077345190883094097636019771377866339531511965136650567412363889183159616188449263752475328663245311059988337996047359263288837436305588848044572937759424466586870280512424336807064729894515840552404756879590698797046333336445465120445087587621743906624279621779634772378802959109714400516183718323267273824736540168545946444437586299214110424738159957388350785999348535171553569373088251552712391288365295267665691357719616011613628772175

n = 27359727711584277234897157724055852794019216845229798938655814269460046384353568138598567755392559653460949444557879120040796798142218939251844762461270251672399546774067275348291003962551964648742053215424620256999345448398805278592777049668281558312871773979931343097806878701114056030041506690476954254006592555275342579529625231194321357904668512121539514880704046969974898412095675082585315458267591016734924646294357666924293908418345508902112711075232047998775303603175363964055048589769318562104883659754974955561725694779754279606726358588862479198815999276839234952142017210593887371950645418417355912567987
c = 3788529784248255027081674540877016372807848222776887920453488878247137930578296797437647922494510483767651150492933356093288965943741570268943861987024276610712717409139946409513963043114463933146088430004237747163422802959250296602570649363016151581364006795894226599584708072582696996740518887606785460775851029814280359385763091078902301957226484620428513604630585131511167015763190591225884202772840456563643159507805711004113901417503751181050823638207803533111429510911616160851391754754434764819568054850823810901159821297849790005646102129354035735350124476838786661542089045509656910348676742844957008857457

n = 27545937603751737248785220891735796468973329738076209144079921449967292572349424539010502287564030116831261268197384650511043068738911429169730640135947800885987171539267214611907687570587001933829208655100828045651391618089603288456570334500533178695238407684702251252671579371018651675054368606282524673369983034682330578308769886456335818733827237294570476853673552685361689144261552895758266522393004116017849397346259119221063821663280935820440671825601452417487330105280889520007917979115568067161590058277418371493228631232457972494285014767469893647892888681433965857496916110704944758070268626897045014782837
c = 14069112970608895732417039977542732665796601893762401500878786871680645798754783315693511261740059725171342404186571066972546332813667711135661176659424619936101038903439144294886379322591635766682645179888058617577572409307484708171144488708410543462972008179994594087473935638026612679389759756811490524127195628741262871304427908481214992471182859308828778119005750928935764927967212343526503410515793717201360360437981322576798056276657140363332700714732224848346808963992302409037706094588964170239521193589470070839790404597252990818583717869140229811712295005710540476356743378906642267045723633874011649259842

n = 25746162075697911560263181791216433062574178572424600336856278176112733054431463253903433128232709054141607100891177804285813783247735063753406524678030561284491481221681954564804141454666928657549670266775659862814924386584148785453647316864935942772919140563506305666207816897601862713092809234429096584753263707828899780979223118181009293655563146526792388913462557306433664296966331469906428665127438829399703002867800269947855869262036714256550075520193125987011945192273531732276641728008406855871598678936585324782438668746810516660152018244253008092470066555687277138937298747951929576231036251316270602513451
c = 17344284860275489477491525819922855326792275128719709401292545608122859829827462088390044612234967551682879954301458425842831995513832410355328065562098763660326163262033200347338773439095709944202252494552172589503915965931524326523663289777583152664722241920800537867331030623906674081852296232306336271542832728410803631170229642717524942332390842467035143631504401140727083270732464237443915263865880580308776111219718961746378842924644142127243573824972533819479079381023103585862099063382129757560124074676150622288706094110075567706403442920696472627797607697962873026112240527498308535903232663939028587036724

n = 23288486934117120315036919418588136227028485494137930196323715336208849327833965693894670567217971727921243839129969128783853015760155446770590696037582684845937132790047363216362087277861336964760890214059732779383020349204803205725870225429985939570141508220041286857810048164696707018663758416807708910671477407366098883430811861933014973409390179948577712579749352299440310543689035651465399867908428885541237776143404376333442949397063249223702355051571790555151203866821867908531733788784978667478707672984539512431549558672467752712004519300318999208102076732501412589104904734983789895358753664077486894529499
c = 10738254418114076548071448844964046468141621740603214384986354189105236977071001429271560636428075970459890958274941762528116445171161040040833357876134689749846940052619392750394683504816081193432350669452446113285638982551762586656329109007214019944975816434827768882704630460001209452239162896576191876324662333153835533956600295255158377025198426950944040643235430211011063586032467724329735785947372051759042138171054165854842472990583800899984893232549092766400510300083585513014171220423103452292891496141806956300396540682381668367564569427813092064053993103537635994311143010708814851867239706492577203899024

n = 19591441383958529435598729113936346657001352578357909347657257239777540424811749817783061233235817916560689138344041497732749011519736303038986277394036718790971374656832741054547056417771501234494768509780369075443550907847298246275717420562375114406055733620258777905222169702036494045086017381084272496162770259955811174440490126514747876661317750649488774992348005044389081101686016446219264069971370646319546429782904810063020324704138495608761532563310699753322444871060383693044481932265801505819646998535192083036872551683405766123968487907648980900712118052346174533513978009131757167547595857552370586353973
c = 3834917098887202931981968704659119341624432294759361919553937551053499607440333234018189141970246302299385742548278589896033282894981200353270637127213483172182529890495903425649116755901631101665876301799865612717750360089085179142750664603454193642053016384714515855868368723508922271767190285521137785688075622832924829248362774476456232826885801046969384519549385428259591566716890844604696258783639390854153039329480726205147199247183621535172450825979047132495439603840806501254997167051142427157381799890725323765558803808030109468048682252028720241357478614704610089120810367192414352034177484688502364022887

n = 19254242571588430171308191757871261075358521158624745702744057556054652332495961196795369630484782930292003238730267396462491733557715379956969694238267908985251699834707734400775311452868924330866502429576951934279223234676654749272932769107390976321208605516299532560054081301829440688796904635446986081691156842271268059970762004259219036753174909942343204432795076377432107630203621754552804124408792358220071862369443201584155711893388877350138023238624566616551246804054720492816226651467017802504094070614892556444425915920269485861799532473383304622064493223627552558344088839860178294589481899206318863310603
c = 6790553533991297205804561991225493105312398825187682250780197510784765226429663284220400480563039341938599783346724051076211265663468643826430109013245014035811178295081939958687087477312867720289964506097819762095244479129359998867671811819738196687884696680463458661374310994610760009474264115750204920875527434486437536623589684519411519100170291423367424938566820315486507444202022408003879118465761273916755290898112991525546114191064022991329724370064632569903856189236177894007766690782630247443895358893983735822824243487181851098787271270256780891094405121947631088729917398317652320497765101790132679171889

n = 26809700251171279102974962949184411136459372267620535198421449833298448092580497485301953796619185339316064387798092220298630428207556482805739803420279056191194360049651767412572609187680508073074653291350998253938793269214230457117194434853888765303403385824786231859450351212449404870776320297419712486574804794325602760347306432927281716160368830187944940128907971027838510079519466846176106565164730963988892400240063089397720414921398936399927948235195085202171264728816184532651138221862240969655185596628285814057082448321749567943946273776184657698104465062749244327092588237927996419620170254423837876806659
c = 386213556608434013769864727123879412041991271528990528548507451210692618986652870424632219424601677524265011043146748309774067894985069288067952546139416819404039688454756044862784630882833496090822568580572859029800646671301748901528132153712913301179254879877441322285914544974519727307311002330350534857867516466612474769753577858660075830592891403551867246057397839688329172530177187042229028685862036140779065771061933528137423019407311473581832405899089709251747002788032002094495379614686544672969073249309703482556386024622814731015767810042969813752548617464974915714425595351940266077021672409858645427346
思路

每两个n求一次公约数,看看是否有不为1的一组n,如果有的话,p就是这个公约数,之后就可以求其他量了

exp.py

Python
from Crypto.Util.number import *
import gmpy2

e = 65537

n = [n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17,n18,n19]
c = [c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19]

for i in range(len(n)):
    for j in range(len(n)):
        if (i!=j):
            t = gmpy2.gcd(n[i],n[j])
            if t != 1:
                p = t
                q = n[i] // p
                d = gmpy2.invert(e,(p-1)*(q-1))
                m = pow(c[i],d,n[i])
                print(long_to_bytes(m))

#flag{abdcbe5fd94e23b3de429223ab9c2fdf}

第二种情况 用中国剩余定理

简要说明中国剩余定理的作用就是把一个同余式组变成一个同余式,eg:

\[ c_1 \equiv m^e \mod n_1 \]
\[ c_2 \equiv m ^e \mod n_2 \]
\[ c_3 \equiv m ^e \mod n_3 \]

转换为一个\(C \equiv m ^ e \mod N\)\(N = lcm(n_1,n_2,n_3)\)

例题1

task.py

Python
import libnum
import gmpy2
import random
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)

p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n1 = p * q
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n2 = p * q
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n3 = p * q
while 1:
    e = random.randint(10, 20)
    print(e)
    if gmpy2.is_prime(e):
        break
c1 = pow(m, e, n1)
c2 = pow(m, e, n2)
c3 = pow(m, e, n3)

print("n1=", n1)
print("n2=", n2)
print("n3=", n3)

print("c1=", c1)
print("c2=", c2)
print("c3=", c3)

先根据中国剩余定理求出\(m^e\),这题的e只给范围,要爆破一下

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

def Get_Mi(m_list,m):               #求衍数
    M_list = []
    for mi in m_list:
        M_list.append(m // mi)
    return M_list

def Get_resMi(M_list,m_list):       #求乘率
    resM_list = []
    for i in range(len(M_list)):
        resM_list.append(gmpy2.invert(M_list[i],m_list[i]))
    return resM_list

def Get_result(a_list):
    M_list = Get_Mi(m_list,m)
    resM_list = Get_resMi(M_list,m_list)
    result = 0
    for i in range(len(M_list)):
        result += M_list[i]*resM_list[i]*a_list[i]
    result %= m
    return result

n1 = 16655230929893303490818415854457831426545038662809855283873228642358207995734291242944120042612699642460820594813654718158395826755230956722936107927889550129166619245152453353908373751380196656611349200623414836128383308653618062999595622747482867683840133843768870236300348203389275090871132570173650238774275209757683812077533989960172822335488251744796657926473009279723460304257252876756936524918018903158795894385111046938638194925881670388700872760201130485273663156422785999102754192840209476417602399017445296045070405343876349687582470436774316410697773759057621576657298096301937899052773787116133124199739
n2 = 17197151926745749646602149115445210421300330711044282276861045275221683290586877554048509794473112203880585601275129330843554946888863132721219683639579200702355880529569042889789589005950061966309684759066705732225014741164779016525568884409690021988879475589545329149547046975086877521757237117008484775731784935960191717287332176327498377273179740487245459081737196777751408106728622513560888261855065717079007065635401835089216224111969668029246916986663313301660909538148574652809266532053889578734157117390082522831069594417637550812101652367765364077901612478983024721669687150628356918237152414368862535409859
n3 = 18719461901666732419189610536735130974364055134601694187780706398369536769336080321122034942831217438281120989017698755904233940669427542442488330152862658754609065361849067002424120904308655036927580582916373684567047102601602588472175947665724244201887952599804681827419266055359412641159981152796138695901074514583606207162167385730873921563442166111892785482387108299191119048884993267729877797586421940344366636285656854837470348603688925980178978612114344024951042846249621559376348599687263736342957456838732355009637030035658212442442824658869094581324944034490700706979663405137522294780606800571433058912041

c1 = 3530761236149189046680124371485374220252032991305088864647979778627799354583229731576585900490173357726425570018182390597284149666834264690795437972634538596441103368165128688664787322126097802985602065455316693754513332761284857157982201975554297034291092099307950246864758375115838291339394148547902128382917596947095456178572561422004708150053706114994560773293625641699691472764190426577272488084620105693964419578988589192873196530900413833531923536786853211065167782657153004342018675666293830194703777994380600060782651326623229047839109994778831432598543154184891096335217588164509636922274833553782288823349
c2 = 7557835478962501903223351987016911891266554255050134264644805724502475848487138948948076311894495847429608136390014902405718084026208891531815323418707377349405409096779206918831458396991602033494429461919844309109113047361743133772636443322195238900874786548606687215969337920658068807801603115402783849082788527952834594443136756890747461628705174983562847145588532659589787532039981477468881131005056101092222499397893730056830156407331988257383965698358904379729558105489119604343081747549319382873235286788453435066434264212954607441597606413293491628299838317713381567250093086011058119721189087729804152444980
c3 =8471234074077377509408346140986116360421840978074779990698043926601850838824365885362094731657766299393262223086536737448516669969503891677808275285733096884405583100485903641986516527279324847718603091709062689898441711907846902050004165404073776422495381050861998133576074526490209080137421773440295749900582039873013319584167081936219517593826232230971430937112005615502869367413205660317010303160932970748420125111225082886082306332340892549579826854620461821084886193470846195356695313518639669516456574134135244251956477677377976434266541164893562226872334598362396368708087248848222008201970781942468960022694

m_list= [n1,n2,n3]
a_list= [c1,c2,c3]

m = 1
for i in m_list:
    m *= i

result = Get_result(a_list)
for e in range(10,20):
    m = gmpy2.iroot(result,e)
    if m[1]:
        print(long_to_bytes(m[0]))

例题2 鹤城杯2021——Crazy_Tech_RSA

task.py

Python
from Crypto.Util.number import *
from Crypto.Util.Padding import *

FLAG = bytes_to_long(pad(b"flag{??????}",64))
def init_key():
    p, q = getPrime(512), getPrime(512)
    n = p*q
    e = 9
    while(GCD((p-1)*(q-1),e)!=1):
        p, q = getPrime(512), getPrime(512)
        n = p*q
    d = inverse(e,(p-1)*(q-1))
    return n,e,d

n_list=list()
c_list=list()
for i in range(9):
    N,e,d=init_key()
    n_list.append(N)
    c=pow(FLAG,e,N)
    c_list.append(pow(FLAG,e,N))
    assert(pow(c,d,N)==FLAG)
print("n_list:",n_list)
print("c_list:",c_list)

先用中国剩余定理解出\(m^e\)

再开根号

这道题的e是给出,如果e没有给出的话,需要爆破e

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

def Get_Mi(m_list,m):               #求衍数
    M_list = []
    for mi in m_list:
        M_list.append(m // mi)
    return M_list

def Get_resMi(M_list,m_list):       #求乘率
    resM_list = []
    for i in range(len(M_list)):
        resM_list.append(gmpy2.invert(M_list[i],m_list[i]))
    return resM_list

def Get_result(a_list):
    M_list = Get_Mi(m_list,m)
    resM_list = Get_resMi(M_list,m_list)
    result = 0
    for i in range(len(M_list)):
        result += M_list[i]*resM_list[i]*a_list[i]
    result %= m
    return result
m_list=[71189786319102608575263218254922479901008514616376166401353025325668690465852130559783959409002115897148828732231478529655075366072137059589917001875303598680931962384468363842379833044123189276199264340224973914079447846845897807085694711541719515881377391200011269924562049643835131619086349617062034608799, 92503831027754984321994282254005318198418454777812045042619263533423066848097985191386666241913483806726751133691867010696758828674382946375162423033994046273252417389169779506788545647848951018539441971140081528915876529645525880324658212147388232683347292192795975558548712504744297104487514691170935149949, 100993952830138414466948640139083231443558390127247779484027818354177479632421980458019929149817002579508423291678953554090956334137167905685261724759487245658147039684536216616744746196651390112540237050493468689520465897258378216693418610879245129435268327315158194612110422630337395790254881602124839071919, 59138293747457431012165762343997972673625934330232909935732464725128776212729547237438509546925172847581735769773563840639187946741161318153031173864953372796950422229629824699580131369991913883136821374596762214064774480548532035315344368010507644630655604478651898097886873485265848973185431559958627423847, 66827868958054485359731420968595906328820823695638132426084478524423658597714990545142120448668257273436546456116147999073797943388584861050133103137697812149742551913704341990467090049650721713913812069904136198912314243175309387952328961054617877059134151915723594900209641163321839502908705301293546584147, 120940513339890268554625391482989102665030083707530690312336379356969219966820079510946652021721814016286307318930536030308296265425674637215009052078834615196224917417698019787514831973471113022781129000531459800329018133248426080717653298100515701379374786486337920294380753805825328119757649844054966712377, 72186594495190221129349814154999705524005203343018940547856004977368023856950836974465616291478257156860734574686154136925776069045232149725101769594505766718123155028300703627531567850035682448632166309129911061492630709698934310123778699316856399909549674138453085885820110724923723830686564968967391721281, 69105037583161467265649176715175579387938714721653281201847973223975467813529036844308693237404592381480367515044829190066606146105800243199497182114398931410844901178842049915914390117503986044951461783780327749665912369177733246873697481544777183820939967036346862056795919812693669387731294595126647751951, 76194219445824867986050004226602973283400885106636660263597964027139613163638212828932901192009131346530898961165310615466747046710743013409318156266326090650584190382130795884514074647833949281109675170830565650006906028402714868781834693473191228256626654011772428115359653448111208831188721505467497494581]
a_list=[62580922178008480377006528793506649089253164524883696044759651305970802215270721223149734532870729533611357047595181907404222690394917605617029675103788705320032707977225447998111744887898039756375876685711148857676502670812333076878964148863713993853526715855758799502735753454247721711366497722251078739585, 46186240819076690248235492196228128599822002268014359444368898414937734806009161030424589993541799877081745454934484263188270879142125136786221625234555265815513136730416539407710862948861531339065039071959576035606192732936477944770308784472646015244527805057990939765708793705044236665364664490419874206900, 85756449024868529058704599481168414715291172247059370174556127800630896693021701121075838517372920466708826412897794900729896389468152213884232173410022054605870785910461728567377769960823103334874807744107855490558726013068890632637193410610478514663078901021307258078678427928255699031215654693270240640198, 14388767329946097216670270960679686032536707277732968784379505904021622612991917314721678940833050736745004078559116326396233622519356703639737886289595860359630019239654690312132039876082685046329079266785042428947147658321799501605837784127004536996628492065409017175037161261039765340032473048737319069656, 1143736792108232890306863524988028098730927600066491485326214420279375304665896453544100447027809433141790331191324806205845009336228331138326163746853197990596700523328423791764843694671580875538251166864957646807184041817863314204516355683663859246677105132100377322669627893863885482167305919925159944839, 2978800921927631161807562509445310353414810029862911925227583943849942080514132963605492727604495513988707849133045851539412276254555228149742924149242124724864770049898278052042163392380895275970574317984638058768854065506927848951716677514095183559625442889028813635385408810698294574175092159389388091981, 16200944263352278316040095503540249310705602580329203494665614035841657418101517016718103326928336623132935178377208651067093136976383774189554806135146237406248538919915426183225265103769259990252162411307338473817114996409705345401251435268136647166395894099897737607312110866874944619080871831772376466376, 31551601425575677138046998360378916515711528548963089502535903329268089950335615563205720969393649713416910860593823506545030969355111753902391336139384464585775439245735448030993755229554555004154084649002801255396359097917380427525820249562148313977941413268787799534165652742114031759562268691233834820996, 25288164985739570635307839193110091356864302148147148153228604718807817833935053919412276187989509493755136905193728864674684139319708358686431424793278248263545370628718355096523088238513079652226028236137381367215156975121794485995030822902933639803569133458328681148758392333073624280222354763268512333515]

m = 1
for i in m_list:
    m *= i

result = Get_result(a_list)
e = 9
m = gmpy2.iroot(result,e)
if m[1]:
print(long_to_bytes(m[0]))

七、维纳攻击

攻击条件:\(d < \frac{1}{3}N^{0.25}\)

原理:

\[ ed \equiv 1 \mod \phi(n) \]

\[ ed = k\phi(n) + 1 \]

因为\(\phi(n) \approx n\)

\[ \therefore ed \approx kn + 1 \]

两边同除\(dn\)

\[ \frac{e}{n} \approx \frac{k}{d} + \frac{1}{dn} \]

\[ \frac{e}{n} - \frac{k}{d} \approx \frac{1}{dn} \]

满足勒让德定理(这个定理网上可以搜得到,根据这个定理,不同形式的n是不一样的,比如说这个例子需要\(n=p\times q\)才可使用),因此\(\frac{e}{n}\)可以作为\(\frac{k}{d}\)的收敛子

连分数展开即可

例题

task.py

Python
import libnum
import random
import gmpy2

## 生成随机素数
p = libnum.generate_prime(512)
q = libnum.generate_prime(512)
m = "flag{20d6e2da95dcc1fa5f5432a436c4be18}"
## 字符串转数字
m = libnum.s2n(m)
n = p * q
phi_n = (p - 1) * (q - 1)

## 计算d
while True:
    nbits = 1024
    d = random.getrandbits(nbits // 4)
    if (libnum.gcd(d, phi_n) == 1 and 36 * pow(d, 4) < n):
        break
## 计算e
e = libnum.invmod(d, phi_n)

c = pow(m, e, n)

print("n=", n)
print("e=", e)
print("c=", c)

exp.py

Python
import gmpy2
import libnum


def continuedFra(x, y):
    """计算连分数
    :param x: 分子
    :param y: 分母
    :return: 连分数列表
    """
    cf = []
    while y:
        cf.append(x // y)
        x, y = y, x % y
    return cf


def gradualFra(cf):
    """计算传入列表最后的渐进分数
    :param cf: 连分数列表
    :return: 该列表最后的渐近分数
    """
    numerator = 0
    denominator = 1
    for x in cf[::-1]:
        ## 这里的渐进分数分子分母要分开
        numerator, denominator = denominator, x * denominator + numerator
    return numerator, denominator


def solve_pq(a, b, c):
    """使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
    :param a:x^2的系数
    :param b:x的系数
    :param c:pq
    :return:p,q
    """
    par = gmpy2.isqrt(b * b - 4 * a * c)
    return (-b + par) // (2 * a), (-b - par) // (2 * a)


def getGradualFra(cf):
    """计算列表所有的渐近分数
    :param cf: 连分数列表
    :return: 该列表所有的渐近分数
    """
    gf = []
    for i in range(1, len(cf) + 1):
        gf.append(gradualFra(cf[:i]))
    return gf


def wienerAttack(e, n):
    """
    :param e:
    :param n:
    :return: 私钥d
    """
    cf = continuedFra(e, n)
    gf = getGradualFra(cf)
    for d, k in gf:
        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 d


n = 113881698992379349039968368927979997900777221951663104697020683691495129639829918739755194174063944178083527489820939138302751895652076620380510013941997706327553964127612610209509889011613768847759318892303231846117914554931459295347697888260576901354448014917692680573408654658384481284699735788978230690197
e = 39068960413447607023613035707248214114819409621234801785480423979473767995171860917209502861408393208940683687475760366491413173744775811644295874981290403938714121977201901942939425294427737703229098649131737380098596135730392902019429964095866394165971291108245774407908011073271822915371753470010435225545
c = 32897925577913728659288168937025744709859960639901500169867896018406263110205704273203287172003057450591000201857719871686024077615520906540631374442504017489026298422189715372129838501090730593164075113452055617571409044743698645392909829425374093273187125709095368164744188182156849031225036001381531504057

d = wienerAttack(e, n)
print(d)
m = pow(c, d, n)
print(libnum.n2s(m).decode())

exp也可以用sagemath写,这里就不展示了

多因子的维纳攻击

例题来源:2023NKCTF——complex_matrix

task.py

Python
from Crypto.Util.number import *
import gmpy2 as gy
flag = ''

k = 400
p, q = getPrime(741), getPrime(741)
N = p * q
phi = (p-1) * (q-1)
_flag = bytes_to_long(flag)
p, q = getPrime(1024), getPrime(1024)
d_array = [getPrime(k) for _ in range(4)] 
e_array = [inverse(i, phi) for i in d_array]
c = pow(_flag, 65537, N)
print('N:',N)
print('e:',e_array)
print('c:',c)

#N: 71841248095369087024928175623295380241516644434969868335504061065977014103487197287619667598363486210886674500469383623511906399909335989202774281795855975972913438448899231650449810696539722877903606541112937729384851506921675290984316325565141178015123381439392534417225128922398194700511937668809140024838070124095703585627058463137549632965723304713166804084673075651182998654091113119667582720831809458721072371364839503563819080226784026253
#e: [65128799196671634905309494529154568614228788035735808211836905142007976099865571126946706559109393187772126407982007858423859147772762638898854472065889939549916077695303157760259717113616428849798058080633047516455513870697383339784816006154279428812359241282979297285283850338964993773227397528608557211742425548651971558377656644211835094019462699301650412862894391885325969143805924684662849869947172175608502179438901337558870349697233790535, 58756559706647121529575085912021603170286163639572075337348109911506627489265537716060463072086480156516641723700802217411122982693536541892986623158818442274840863016647800896033363360822503445344748132842451806511693779600370832206455202293028402486647422212959763287987847280322100701242139127654031151565924132562837893975505159702015125483479126108892709063135006366792197127007229210558758401679638300464111782814561428899998471531067163715, 34828685390969672139784723764579499920301439564705391196519314224159563070870933754477650614819514127121146216049444888554338415587165719098661141454627820126445291802801256297252654045398330613075575527685542980264993711077876535643646746742646371967302159565887123638001580042027272379341650995728849759541960087953160211696369079708787543303742132161742979856720539914370868829868891655221361545648778590685232034703220732697083024449894197969, 26717968456600556973167180286909817773394160817933525240720067057464671317174201540556176814203780603153696663101158205367554829261808020426363683474848952397963507069306452835776851274959389849223566030857588019845781623271395012194869024566879791449466064832273531795430185178486425688475688634844530106740480643866537205900809400383304665727460014210405339697947582657505028211149470787536144302545259243549176816653560626044921521516818788487]
#c: 39297018404565022956251803918747154798377576057123078716166221329195959669756819453426741569480551313085435037629493881038383709458043802420338889323233368852331387845200216275712388921820794980987541224782392553528127093154957890356084331463340193478391679540506421250562554424770350351514435220782124981277580072039637811543914983033300225131364246910828188727043248991987332274929827173923543187017105236008487756190002204169623313222748976369

存个脚本,来源文章列表 | NSSCTF

exp.py

Python
import gmpy2
import libnum
isdigit = lambda x: ord('0') <= ord(x) <= ord('9')

def my_permutations(g, n):
    sub = []
    res = []
    def dfs(s, prev):
        if len(s) == n:
            res.append(s[::])
        for i in g:
            if i in s or i < prev:
                continue
            s.append(i)
            dfs(s, max(prev, i))
            s.remove(i)
    dfs(sub, 0)
    return res

class X3NNY(object):
    def __init__(self, exp1, exp2):
        self.exp1 = exp1
        self.exp2 = exp2

    def __mul__(self, b):
        return X3NNY(self.exp1 * b.exp1, self.exp2 * b.exp2)

    def __repr__(self):
        return '%s = %s' % (self.exp1.expand().collect_common_factors(), self.exp2)

class X_Complex(object):
    def __init__(self, exp):
        i = 0
        s = '%s' % exp
        while i < len(s):
            if isdigit(s[i]):
                num = 0
                while i < len(s) and isdigit(s[i]):
                    num = num*10 + int(s[i])
                    i += 1
                if i >= len(s):
                    self.b = num
                elif s[i] == '*':
                    self.a = num
                    i += 2
                elif s[i] == '/':
                    i += 1
                    r = 0
                    while i < len(s) and isdigit(s[i]):
                        r = r*10 + int(s[i])
                        i += 1
                    self.b = num/r
            else:
                i += 1
        if not hasattr(self, 'a'):
            self.a = 1
        if not hasattr(self, 'b'):
            self.b = 0

def WW(e, d, k, g, N, s):
    return X3NNY(e*d*g-k*N, g+k*s)
def GG(e1, e2, d1, d2, k1, k2):
    return X3NNY(e1*d1*k2- e2*d2*k1, k2 - k1)

def W(i):
    e = eval("e%d" % i)
    d = eval("d%d" % i)
    k = eval("k%d" % i)
    return WW(e, d, k, g, N, s)

def G(i, j):
    e1 = eval("e%d" % i)
    d1 = eval("d%d" % i)
    k1 = eval("k%d" % i)

    e2 = eval("e%d" % j)
    d2 = eval("d%d" % j)
    k2 = eval("k%d" % j)

    return GG(e1, e2, d1, d2, k1, k2)

def R(e, sn): ## min u max v
    ret = X3NNY(1, 1)
    n = max(e)
    nn = len(e)
    l = set(i for i in range(1, n+1))
    debug = ''
    u, v = 0, 0
    for i in e:
        if i == 1:
            ret *= W(1)
            debug += 'W(%d)' % i
            nn -= 1
            l.remove(1)
            u += 1
        elif i > min(l) and len(l) >= 2*nn:
            ret *= G(min(l), i)
            nn -= 1
            debug += 'G(%d, %d)' % (min(l), i)
            l.remove(min(l))
            l.remove(i)
            v += 1
        else:
            ret *= W(i)
            l.remove(i)
            debug += 'W(%d)' % i
            nn -= 1
            u += 1
    ## print(debug, end = ' ')
    return ret, u/2 + (sn - v) * a

def H(n):
    if n == 0:
        return [0]
    if n == 2:
        return [(), (1,), (2,), (1, 2)]
    ret = []
    for i in range(3, n+1):
        ret.append((i,))
        for j in range(1, i):
            for k in my_permutations(range(1, i), j):
                ret.append(tuple(k + [i]))
    return H(2) + ret

def CC(exp, n):
    cols = [0 for i in range(1<<n)]

    ## split exp
    texps = ('%s' % exp.exp1.expand()).strip().split(' - ')
    ops = []
    exps = []
    for i in range(len(texps)):
        if texps[i].find(' + ') != -1:
            tmp = texps[i].split(' + ')
            ops.append(0)
            exps.append(tmp[0])
            for i in range(1, len(tmp)):
                ops.append(1)
                exps.append(tmp[i])
        else:
            ops.append(0)
            exps.append(texps[i])
    if exps[0][0] == '-':
        for i in range(len(exps)):
            ops[i] = 1-ops[i]
        exps[0] = exps[0][1:]
    else:
        ops[0] = 1
    ## find e and N
    l = []
    for i in range(len(exps)):
        tmp = 1 if ops[i] else -1
        en = []
        j = 0
        while j < len(exps[i]):
            if exps[i][j] == 'e':
                num = 0
                j += 1
                while isdigit(exps[i][j]):
                    num = num*10 + int(exps[i][j])
                    j += 1
                tmp *= eval('e%d' % num)
                en.append(num)
            elif exps[i][j] == 'N':
                j += 1
                num = 0
                if exps[i][j] == '^':
                    j += 1
                    while isdigit(exps[i][j]):
                        num = num*10 + int(exps[i][j])
                        j += 1
                if num == 0:
                    num = 1
                tmp *= eval('N**%d' % num)
            else:
                j += 1
        if tmp == 1 or tmp == -1:
            l.append((0, ()))
        else:
            l.append((tmp, tuple(sorted(en))))

    ## construct h
    mp = H(n)
    for val, en in l:
        cols[mp.index(en)] = val
    ## print(cols)
    return cols

def EWA(n, elist, NN, alpha):
    mp = H(n)
    var('a')
    S = [X_Complex(n*a)]
    cols = [[1 if i == 0 else 0 for i in range(2^n)]]
    for i in mp[1:]:
        eL, s = R(i, n)
        cols.append(CC(eL, n))
        S.append(X_Complex(s))

    alphaA,alphaB = 0, 0
    for i in S:
        alphaA = max(i.a, alphaA)
        alphaB = max(i.b, alphaB)
    ## print(alphaA, alphaB)
    D = []
    for i in range(len(S)):
        ## print((alphaA-S[i].a), (alphaB - S[i].b))
        D.append(
            int(NN^((alphaA-S[i].a)*alpha + (alphaB - S[i].b)))
        )
    kw = {'N': NN}
    for i in range(len(elist)):
        kw['e%d' % (i+1)] = elist[i]

    B = Matrix(ZZ, Matrix(cols).T(**kw)) * diagonal_matrix(ZZ, D)
    L = B.LLL(0.5)
    v = Matrix(ZZ, L[0])
    x = v * B**(-1)
    phi = int(x[0,1]/x[0,0]*elist[0])
    return phi

def attack(NN, elist, alpha):
    phi = EWA(len(elist), elist, NN, alpha)
    print(phi)
    return phi


NN = 71841248095369087024928175623295380241516644434969868335504061065977014103487197287619667598363486210886674500469383623511906399909335989202774281795855975972913438448899231650449810696539722877903606541112937729384851506921675290984316325565141178015123381439392534417225128922398194700511937668809140024838070124095703585627058463137549632965723304713166804084673075651182998654091113119667582720831809458721072371364839503563819080226784026253
elist = [65128799196671634905309494529154568614228788035735808211836905142007976099865571126946706559109393187772126407982007858423859147772762638898854472065889939549916077695303157760259717113616428849798058080633047516455513870697383339784816006154279428812359241282979297285283850338964993773227397528608557211742425548651971558377656644211835094019462699301650412862894391885325969143805924684662849869947172175608502179438901337558870349697233790535, 58756559706647121529575085912021603170286163639572075337348109911506627489265537716060463072086480156516641723700802217411122982693536541892986623158818442274840863016647800896033363360822503445344748132842451806511693779600370832206455202293028402486647422212959763287987847280322100701242139127654031151565924132562837893975505159702015125483479126108892709063135006366792197127007229210558758401679638300464111782814561428899998471531067163715, 34828685390969672139784723764579499920301439564705391196519314224159563070870933754477650614819514127121146216049444888554338415587165719098661141454627820126445291802801256297252654045398330613075575527685542980264993711077876535643646746742646371967302159565887123638001580042027272379341650995728849759541960087953160211696369079708787543303742132161742979856720539914370868829868891655221361545648778590685232034703220732697083024449894197969, 26717968456600556973167180286909817773394160817933525240720067057464671317174201540556176814203780603153696663101158205367554829261808020426363683474848952397963507069306452835776851274959389849223566030857588019845781623271395012194869024566879791449466064832273531795430185178486425688475688634844530106740480643866537205900809400383304665727460014210405339697947582657505028211149470787536144302545259243549176816653560626044921521516818788487]
c = 39297018404565022956251803918747154798377576057123078716166221329195959669756819453426741569480551313085435037629493881038383709458043802420338889323233368852331387845200216275712388921820794980987541224782392553528127093154957890356084331463340193478391679540506421250562554424770350351514435220782124981277580072039637811543914983033300225131364246910828188727043248991987332274929827173923543187017105236008487756190002204169623313222748976369
alpha = 400 / int(NN).bit_length()              #400指的是d的比特
for i in range(1, len(elist)+1):
    var("e%d" % i)
    var("d%d" % i)
    var("k%d" % i)
g, N, s = var('g'), var('N'), var('s')

for i in range(len(elist)):
    elist[i] = Integer(elist[i])
phi = attack(NN, elist, alpha)

d = gmpy2.invert(65537, phi)
m = int(pow(c, d, NN))
print(libnum.n2s(m))
## NKCTF{F10w3r_Hav3_r3start_Day_N0_Man_iS_Y0ung_Aga1n}

八、共享素数

两个n有相同的因子,直接求公因数就好了

例题

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(1024)
q1 = libnum.generate_prime(1024)
q2 = libnum.generate_prime(1024)
n1 = p * q1
n2 = p * q2
e = 65537
c1 = pow(m, e, n1)
c2 = pow(m, e, n2)
print("n1=", n1)
print("n2=", n2)
print("c1=", c1)
print("c2=", c2)

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

n1 =22036096414750333101406538757625812613248444424049684758772058140377463618250795867832853117902163257003301132932490853355450673043991381637053778653821758783435921283322439267628837074056789021611782010444749024512216306961829453158759193969454712080047205883631153040900193575534288719429526169135614695862857156639195562501217424283401364740789214390377624598500939079307163832197534297642842744345373413423859103414676878739674227554029537817890874031594311152843060524913028526429344172693584167398664817727426767686424415189580527939256633114120720308173788544194669684834369132677253418971615644691719536820433
n2 =17251653165250011947306159769694143433212298910745609670733920118632739529605426957617875166211610794383631191273183964010346725508667657137931394653419082978603166138439632713627832321960586938891805262605225424775586397813147240201440036009395991700175612039074317131837185920131565272816639771739150718839592250080325774556601865770352479323350280393818365902570673799584200153846520860657815432981116910233453207519365604533077996449249223280685559491369393277114405166293011856168173778428700718404816038991911974902005969923746846815798515831172402122367026339135245751136695128279659872423027539333024854133659
c1 =1408937404754902028814920445701404613476983383738408959873219805755187459225302977012340464647741276263048769176603562703588286152102079561897286480939341184453940846603761664643956274520695676061184610702912592321357240109665885587701906369214592438887065694062151546241739122722790261888290706296747328780758965559093085406877315628139811554345214347799361309288949307776501119359571116522001853935560023530705364795364624152061166945046882418058831567088890277747158775876953662285842999920594778445526224560016361787941610542255000514402493843548068271595134879195073239601085319795505131267697387692179944010772
c2 =12175155463891225370775786368564999885751076529394005420368968241428058465831204925081389807872272508968531564921996276054143112205083003561546962102395368677755381589762565680900651699885381615975236663522025080384481537727180649424984636506390139648492522347366656322729441290553251505232488994951243521740296115895651926783520321289129993169836271218322014443396220481361007479796234525990954874013729001502984583162486353287639655447340498584591591009224750835072707121652727119580764546240945763088815277130694950594020142961659318737857067755125666395542079926757603295535345281657293981259718286213812909191174
e = 65537

t = gmpy2.gcd(n1,n2)
if t != 1:
    p = t
    q = n1 // p
    d = gmpy2.invert(e,(p-1)*(q-1))
    print(long_to_bytes(pow(c1,d,n1)))

九、dp泄露

原理推导

\[ \because d_p \equiv d \mod (p-1) \]

左右同乘e得

\[ d_p\times e \equiv d\times e \mod (p-1) \]

\[ d\times e = k_1(p-1) +d_p\times e \]
\[ \because d\times e \equiv 1 \mod \phi(n) \]
\[ \therefore d\times e = k_2(p-1)(q-1)+1 \]
\[ \therefore k_1(p-1)+dp×e = k_2(p-1)(q-1)+1 \]

化简得

\[ d_p\times e = (p-1)[k_2(q-1)-k_1] + 1 \]
\[ \because d_p < p-1 \]
\[ \therefore 0< k_2(q-1)-k_1 \]

遍历\(i \in (0,e)\)的所有值,如果\(d_p\times e-1\)能够整除\(i\)则,我们就可以求得\(p-1\)

例题1

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
e = 65537
n = p * q
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
print("d=",d)
dp = d % (p - 1)
c = pow(m, e, n)
print("n=", n)
print("e=", e)
print("c=", c)
print("dp=", dp)

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

n = 17954000555185130679232377513216924086370205717680723426279833171815673097846434355592747466782173529597978954062272371758852339605432963940809244445748900431763988392120156128524694217746804744076348205536430811607694098089294013508111154514184079159181563040241302764793111632189829652065825442796716027051750043654503172469553222784393034067341575558782690547449884992414106322578261286526556856377419622164651917718772498388922129465042492616904419638034789576380038341882418547113136903862482397767904921157094396562770484669239932933837196899640811033424030302285885080504355934919967693498648701839252087380477
c = 1808490145328726635638705291306162339133116115672167390186563455429486046847680827004544020052978814401354685508506555963361065725012891622174617670820924531959433674273579973778657669162210657120785539629874456938159379512635932223403818177582242406964621317531188027900758868453176575753681627336133481701182056262584118645398172089436811174776978418919803975809158758707062473122656821152108292060156091196577573172937093806159669057467094718771367509856968694209472630329096814023864642219783061245777457366202879949123915168571990229277326042767587009623951348541055835347386485900505749475571895673653225741649
dp =114993440308125678369350484461242628405806013372930792337329972720600942891053460367721272956155528263465552939386621554396883112994907299126303463171544968629898544811159986442249990975328659373544491636958873255689808151391606942912468529923159009459712396428051292828093059494024457367637991626193487727331
e =65537

for i in range(1,e):
    t = (dp * e - 1) % i        #这是p-1前面的系数
    if t == 0:
        p = (dp * e - 1) // i + 1
        if n % p == 0:
            q = n // p
            d = gmpy2.invert(e,(p-1)*(q-1))
            print(long_to_bytes(pow(c,d,n)))

例题2 e很大

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
e = libnum.generate_prime(128)
n = p * q
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)

dp = d % (p - 1)
c = pow(m, e, n)
print("n=", n)
print("e=", e)
print("c=", c)
print("dp=", dp)

在e很大的情况下,我们爆破\((0,e)\)的值就显得不现实。需要用其他方法 $$ \because d_p \times e = k(p-1)+1 $$

\[ \therefore dp ×e \equiv 1 \mod (p-1) \]

取一个与\(p\)互素的常数\(a\),则有

\[ a^{d_p\times e} \equiv a^{k(p-1)+1} \equiv a \mod p \]
\[ \therefore a^{dp×e}-a =kp \]

然后与n求公因数即可得到p

exp.py

Python
from Crypto.Util.number import getPrime,long_to_bytes
import gmpy2

n= 18150086749964030204952772593650655354407282168407543480518017821905322058792108409169209539752974177937328703466169798097779231540410352308736251888449285307132274903811942187361234788236374036493935354342502760429195828731474432692281516115002087485890105917067613983079156790015275611914738187418424730456416770000646598861170179898016595068557049151791620868027603590052357726580431487083778514163249201637184318559165989454489485258667170883129099757366657203826319909509544670881866298317938263281638296241860739828567999093491297970389142395080780768892194380031018324855383292685388767246393785705551231780899
e= 278817530653170259039233683650169866189
c= 10813189255953686674328168562592345253502176950586624384555229875947895107088800774325110599057171743065073092283651992594106863499561887773348061509184951920329015539552207920010924284253109754443530283215130327152416667309504455641730629770856098336299767661535526880065006664806270833698497579073641076473649621250873969544362062393319711114090739414415757921473984666129110213417578310035188480139392986280529932137364474426391469019554935239234968517869914506321750458197587849623935306068749435995413807906558975073047215241796461878288003366415184144140612947217041288538376514085070670565572628317388808356563
dp= 25015362092988139281045705051525732988903355826221062953430054346210238749256645995431509510448988062597830708438496723137288693056422683942336451600173722142331077900759633024803467824277121463213906905346623305038031660927650043841899168305106293747254814365869018331032172781810238397508103692878400636333

a = getPrime(10)

p = gmpy2.gcd(pow(a,dp*e,n)-a,n)    #pow(a,dp*e,n)-a 就是kp
m = pow(c,dp,p)
print(long_to_bytes(m))

q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))
关于m = pow(c,dp,p)的证明

很多题目会出现在mod p下求解的情况,需要注意的是前提条件为\(m < p\),证明如下

\[ \because c \equiv m^e \mod n \]
\[ ed \equiv 1 \mod \phi(n) \]
\[ d_p \equiv d \mod p-1 \]

所以有

\[ e \times d_p \equiv 1 \mod p-1 \]

\[ e\times d_p = k \times (p-1) + 1 \]
\[ \therefore c ^{d_p} \equiv m ^{e\times d_p} \equiv m ^{k(p-1)+1} \equiv m \mod p \]

例题3 dp高位泄露

e不是很大
\[ \because ed_p \equiv 1 \mod p-1 \]
\[ \therefore e(d_{phigh} + x) \equiv 1 \mod p-1 \]
\[ \therefore e(d_{phigh} + x) = k(p-1) + 1 \]
\[ \therefore e(d_{phigh}+x) + k - 1\equiv 0 \mod p \]

如果e不大的话,直接爆破k解这个方程即可

e很大

task.py

Python
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
n = p * q
e = getPrime(64)
d = inverse(e,(p-1)*(q-1))

dp = d % (p-1)
leak = dp >> 64 << 64
c = pow(m,e,n)
print(f"n = {n}")
print(f"c = {c}")
print(f"e = {e}")
print(f"leak = {leak}")
"""
n = 108867023585283899312601516631596145998654767437612286040742902945694826711187570537876759789643259431350352590412655898964386234903475482280911808566497936037152171435317938413515356868240854632004961172899360272761406519077380255802700944002126599829184693692273637230349297388495886988988512631044494610227
c = 55610391226438079690691299914193010897347055835338032942001645697966587672596937470425142446679815934940962159432814479063990621696064239924347523289515556844359094065042974221093712989000153444928594999529082105262409273370242062803004814801742763253605838374098344711160380314529928211369000469059015996052
e = 14257237624892167181
leak = 2451130625204471881762267484243564297625381377098677997721378436579042927693972537775392209546509688501306219327699327137859174235486735043842710630825984
"""
\[ \because ed_p \equiv 1 \mod p-1 \]
\[ \therefore e(d_{phigh} + x) \equiv 1 \mod p-1 \]
\[ \therefore e(d_{phigh} + x) = k(p-1) + 1 \]
\[ \therefore e(d_{phigh}+x) + k - 1\equiv 0 \mod p \]

k和x都是64bit的值,用二元copper即可

exp

Python
from Crypto.Util.number import *
import gmpy2
import itertools

def small_roots(f, bounds, m=1, d=None):
    if not d:
        d = f.degree()
        print(d)
    R = f.base_ring()
    N = R.cardinality()
    f /= f.coefficients().pop(0)
    f = f.change_ring(ZZ)
    G = Sequence([], f.parent())
    for i in range(m + 1):
        base = N ^ (m - i) * f ^ i
        for shifts in itertools.product(range(d), repeat=f.nvariables()):
            g = base * prod(map(power, f.variables(), shifts))
            G.append(g)
    B, monomials = G.coefficient_matrix()
    monomials = vector(monomials)
    factors = [monomial(*bounds) for monomial in monomials]
    for i, factor in enumerate(factors):
        B.rescale_col(i, factor)
    B = B.dense_matrix().LLL()
    B = B.change_ring(QQ)
    for i, factor in enumerate(factors):
        B.rescale_col(i, 1 / factor)
    H = Sequence([], f.parent().change_ring(QQ))
    for h in filter(None, B * monomials):
        H.append(h)
        I = H.ideal()
        if I.dimension() == -1:
            H.pop()
        elif I.dimension() == 0:
            roots = []
            for root in I.variety(ring=ZZ):
                root = tuple(R(root[var]) for var in f.variables())
                roots.append(root)
            return roots
    return []

n = 108867023585283899312601516631596145998654767437612286040742902945694826711187570537876759789643259431350352590412655898964386234903475482280911808566497936037152171435317938413515356868240854632004961172899360272761406519077380255802700944002126599829184693692273637230349297388495886988988512631044494610227
c = 55610391226438079690691299914193010897347055835338032942001645697966587672596937470425142446679815934940962159432814479063990621696064239924347523289515556844359094065042974221093712989000153444928594999529082105262409273370242062803004814801742763253605838374098344711160380314529928211369000469059015996052
e = 14257237624892167181
leak = 2451130625204471881762267484243564297625381377098677997721378436579042927693972537775392209546509688501306219327699327137859174235486735043842710630825984

R.<x,y> = PolynomialRing(Zmod(n))
f = e * (leak + x) + (y - 1)
res = small_roots(f,(2^64,2^64),m=1,d=3)
for root in res:
    dp_low = root[0]
    dp = leak + dp_low
    tmp = pow(2,e*dp,n) - 2
    p = gmpy2.gcd(tmp,n)
    q = n // p
    d = inverse(e,(p-1)*(q-1))
    m = pow(c,d,n)
    print(long_to_bytes(m))

十、dp,dq泄露

如果题目给出\(p,q,d_p,d_q\),那么直接在模p和模q下求解即可,如果模不够大,再用中国剩余定理即可

例题1

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)
p = libnum.generate_prime(256)
q = libnum.generate_prime(256)
e = 65537
n = p * q
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
print("d=", d)
dp = d % (p - 1)
dq = d % (q - 1)
c = pow(m, e, n)

print("p=", p)
print("q=", q)
print("c=", c)
print("dp=", dp)
print("dq=", dq)

exp.sage

Python
import gmpy2
from Crypto.Util.number import *

p = 112454994630978850005784651276022327545786198205744597431888680937657203192943
q = 111081771780978300442208201256251933100607227308819156491182881723714968913833
c = 7847140580627012782899798457736961376953768684667159008470556786390887805253326211691923724846808704462396746105331991924048819814322540306282164012066426
dp = 99016059099144522019375365089687785694029213535292918424815544402513220169503
dq = 79504900574184798493105575420403885224379864982754477219462523963780735261625
n = p*q

mp = pow(c,dp,p)
mq = pow(c,dq,q)
print(long_to_bytes(mp))
print(long_to_bytes(mq))

m = crt([mp,mq],[p,q])
print(long_to_bytes(m))

十一、考察欧拉函数计算

欧拉函数计算方法:

  1. \(n = 1\),则\(\phi(n)= 1\)

  2. \(n\)是素数,则\(\phi(n) = n - 1\)

  3. \(n = p^r\)(\(p\)为素数,\(r\)为大于等于1的整数),则\(\phi(n) = p^{r-1}(p-1)\)

  4. \(n = \prod_{i=1}^kp_i\),则\(\phi(n) = \prod_{i=1}^{k}(p_i-1)\)

例题1

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)
p = libnum.generate_prime(512)
n = p ** 5
e = 65537
c = pow(m, e, n)
print("n=", n)
print("e=", e)
print("c=", c)

本题考查\(n = p^r\)

exp.py

Python
from Crypto.Util.number import *
import gmpy2

n = 109671394618534156590716540772306636060550711465455829526382945168271125218007503161807386153286648328529071790130095763349089936429151343426469415497138306284842512691195220650548494501337831207724397925027651025644976364368511262187227825083865690186591571250267479440100917127211656877566179258870510690665025580200477574123634259670039152125607834855408269848826938902407472374892693266119859709658788565014195766992713646832624001067898323913639712284673673557898135411273837250194079399384826384835709060797351332323930309379071015140548478132323474993964925055399350661600068568602468295748573856804293659942047543032878436316918991716636681116631322082919386178460096115355963882313682994343822128626370850455024905542367255179074441679461101331332093019368492643
e = 65537
c = 83166081100602571613112201467626632459949037182633475449066025299006894059443612622701975752551708249083077180755342065457682925338233793688583913228361068561515321097016501900442865692643652803949920987493441500744743553498018003210308290969303190355756775977367324120441564693220728163622472865572357639056734898022442794130514936425334924283145153496702507951466618177405870656868713199816223765264228006032228439676574053839549440372521050502986043855511540855864034336902304414848547480112182226920285820386643015671106933140105835060319823245206934565859078021518312305243997612804935774414840491355098017977514736057368000596388974956265042651277403519473262257945986199408278855335170546242641727930526697102351648388874227840119205560199015181257296591985205818
p = 10186351850605898834333098258639828910824016865517013383611935945131883947448223014077315468253377357594775340263769153442019216205089695821439736926082483

phi = p**5-p**4
d = gmpy2.invert(e,phi)
print(long_to_bytes(pow(c,d,n)))

例题2

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(512)
q = libnum.generate_prime(512)
n = p ** 3 * q
e = 65537
c = pow(m, e, n)
print("q=", q)
print("n=", n)
print("e=", e)
print("c=", c)

\(\because n = p^{3}×q\)

\(\therefore \phi(n) = (p^{3}-p^{2})×(q-1)\)

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

q = 13345672330679418443866848695749753384841350112452462690350565885192764753702964893062035116023096943358384379827500462723016224486435032522188166109529147
n = 4663711063632671446966617442890809468548735750386480905820144168615122369358088158709822723959108989276525102755551604204514586528122441075489492157644479194671784485228731421500391142158401889177506319977349707503678947872777602384171260874359045585814189677748712566088220734735479518470883541166059233676170283938881683675790979128058243582825012415020019043126740915522571217495119060031729061070896580899732991209113679151755713858497630999746612658308778711613697038750661479517187094033129979714688161348844542070263109259258937863241431480105798790363889436471998002323269704182369356300287043940743500087239
e = 65537
c = 4050303218893912343776312253598257474375000778822229482734626960955864773175090306426885201033332266573903303684635688485414725284644108123459136702775991157244389086147955395682206311752151842740679445903864544823592773331496589661187968392779340028173948172003460012051289357755574536619241969492822938688821287700132262703677149526846356206143053559703859606442209340834979412336031660590507709503830013770022485005101363701272279629510056964018618212014677531338026866748075406283893942248599825641124694672878533987994699411744521757385858889763503705242690212615083634186373933214686193717950163215308138805348

p = n // q 
phi = (p**3-p**2)*(q-1)
d = gmpy2.invert(e,phi)
print(long_to_bytes(pow(c,d,n)))

例题3

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(512)
q = gmpy2.next_prime(p)
r = libnum.generate_prime(512)
n = p * q * r
e = 65537
c = pow(m, e, n)
print("r=", r)
print("n=", n)
print("e=", e)
print("c=", c)

r = 12328943069972158868300333965019293732240349172933398867374450193780676916633106046545397891902123683693837126404908611670219604587587151306224914062663729
n = 928445951911850156618541782850215900925329423880533465612155142015978599609287623859912813317551629221695490535012732781489396534420011145723987610606038091488823086647363964394753700158320900867101659445170118179077194271099520502633316318889163873291574934282498061117736456183503867870294570083013883365868242889035609602940685335912371326827533418614992221705810476710807373254363162373986374486325350746366851935451369892949745302442034805629514003196071631
e = 65537
c = 327716655224470059950709685055600963837116578216483343492948888372401723689223347212508532985781828794786448842515029515358422017875793926582832247025212149474404973170422295165602666360784347416812528617973764432916955654602356835327769633635513894485943553309743509322858937973710628023758816806471875016815994664897320150855163109437521642800230902661034555151514311149333258071668655344069451897282357234220538922127548822361859943829665459953651351620958628
\[ \because n = p\times q \times r \]
\[ \therefore \phi(n)=(p-1)×(q-1)×(r-1) \]

exp.py

Python
from Crypto.Util.number import long_to_bytes
import gmpy2

r = 12328943069972158868300333965019293732240349172933398867374450193780676916633106046545397891902123683693837126404908611670219604587587151306224914062663729
n = 928445951911850156618541782850215900925329423880533465612155142015978599609287623859912813317551629221695490535012732781489396534420011145723987610606038091488823086647363964394753700158320900867101659445170118179077194271099520502633316318889163873291574934282498061117736456183503867870294570083013883365868242889035609602940685335912371326827533418614992221705810476710807373254363162373986374486325350746366851935451369892949745302442034805629514003196071631
e = 65537
c = 327716655224470059950709685055600963837116578216483343492948888372401723689223347212508532985781828794786448842515029515358422017875793926582832247025212149474404973170422295165602666360784347416812528617973764432916955654602356835327769633635513894485943553309743509322858937973710628023758816806471875016815994664897320150855163109437521642800230902661034555151514311149333258071668655344069451897282357234220538922127548822361859943829665459953651351620958628

t = gmpy2.iroot(n//r,2)[0]
p = gmpy2.next_prime(t)
q = n // p // r

phi = (p-1)*(q-1)*(r-1)
d = gmpy2.invert(e,phi)
print(long_to_bytes(pow(c,d,n)))

十二、e和phi不互素

这种情况在比赛中比较常见。在做这类题目之前,建议先了解sagemath基本使用方法

\(e\)\(\phi(n)\)不互素时,则不存在模\(\phi(n)\)的逆元\(d\)使得\(ed \equiv 1(mod \quad \phi(n))\)

处理方式1:

这种处理方式是最简单也是最常规的

\(t = gcd(e,\phi(n))\)

\(e = e'\times t\),则\(e' = \frac{e}{t}\)

\(c \equiv m^e \mod n \longrightarrow c \equiv (m ^t)^{e'} \mod n\)

\(e'\)对应的\(d\),解出\(m^t \equiv c^d \mod n\)

\(m^t < n\)的情况下开根即可获得\(m\)

例题1

task.py

Python
import gmpy2
import libnum
import random
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)

while 1:
    e = random.randint(100, 1000)
    p = libnum.generate_prime(1024)
    q = libnum.generate_prime(1024)
    phi_n = (p - 1) * (q - 1)
    t = gmpy2.gcd(e, phi_n)
    if t == e:
        continue
    t1 = e // t
    if gmpy2.invert(t1, phi_n) and t > 1:
        break
n = p * q
c = pow(m, e, n)
print("p=", p)
print("q=", q)
print("e=", e)
print("c=", c)

exp.py

Python
from Crypto.Util.number import *
import gmpy2

p = 171854505164939390402295426493389586289972154851849140728417624619463988154808053805729538974688869671559032639921300088271234681410193379381085714252211392886408792711387524667824537369266846649573070209815436507363007636943912350208275895292853801665488228125846058987049326903498661007035974420392738723323
q = 145951936627745243523384785325963094339728144811023266133546816860787405503371056873662508073284279180417626507724315776654624382665743082805910036891739754019932290977071276850239245644056698685966997752654383650764557358649666141576105936215709831181842086893228254304235678475375978464394818353375373451573
n = p*q
e = 830
c= 4413268199893347044741276120215584703428167052744516280494996526431559720190092261631829389527634625276020346166956540800884139234489942113764564139232948414263452549927818365096023041932432723988241639527832673120924732407691135173154085803338322715604275530735968992726708155724384432557207264839248502158712330572704509492520346044648676055223193900826626346707083590815897507927683083455678855000344499804465073698745769989966769567497677402668725931090596642504740789789740965769347050166069295727209131555338513809368814890255851742010120871635378654904140016065148709710206173069000137023824698858539843753921

phi = (p-1)*(q-1)
t = gmpy2.gcd(e,phi)
if t != 1:
    print(t)
    e1 = e // t
    d = gmpy2.invert(e1,phi)
    m = pow(c,d,n)
    print(long_to_bytes(gmpy2.iroot(m,t)[0]))

处理方式2:

使用sagemath,建立有限域,然后开根,以一个例题简要说明

task.py

Python
from secret import flag
from Crypto.Util.number import *

m = bytes_to_long(flag)
e = 2
p = getPrime(512)
q = getPrime(512)
n = p * q

c = pow(m,e,n)
print(f"c = {c}")
print(f"p = {p}")
print(f"q = {q}")
print(f"n = {n}")

"""
c = 3136716033729406787100895984031132591372408032397380657110411936279557864804613451803228203558942365572618855741582340710496951283998288077886684480798840909615843832888291672118118174611935731391325961
p = 6995170125828760803175021463861846748101755707034027896253692749933464491557662142860042836548188827042797130278821285624095121174944527912522028416084887
q = 12272700813225623560132355056448185486009112345760462061684545263916365691442580749089054915285013385322910087693364777427389347919562222271368766699836367
n = 85849630091910220195429598000499270755938566349764235961421375317755949910077610512936495968854306764812963460054159547290157103999299511719875933657730205460710045409434205381402766954402709130620384187298033137488697869180531872534818830013412093865496100309052022972165149312556870258314510623053681685529
"""

exp.sage

Python
from Crypto.Util.number import *

c = 3136716033729406787100895984031132591372408032397380657110411936279557864804613451803228203558942365572618855741582340710496951283998288077886684480798840909615843832888291672118118174611935731391325961
p = 6995170125828760803175021463861846748101755707034027896253692749933464491557662142860042836548188827042797130278821285624095121174944527912522028416084887
q = 12272700813225623560132355056448185486009112345760462061684545263916365691442580749089054915285013385322910087693364777427389347919562222271368766699836367
n = 85849630091910220195429598000499270755938566349764235961421375317755949910077610512936495968854306764812963460054159547290157103999299511719875933657730205460710045409434205381402766954402709130620384187298033137488697869180531872534818830013412093865496100309052022972165149312556870258314510623053681685529

R.<x> = PolynomialRing(Zmod(p))
f = x^2 - c
res1 = f.roots()

R.<x> = PolynomialRing(Zmod(q))
f = x^2 - c
res2 = f.roots()

for i in res1:
    for j in res2:
        m = crt([int(i[0]),int(j[0])],[p,q])
        print(long_to_bytes(m))

\(m < p\)的时候,不需要中国剩余定理也能出

两种处理方式的综合

例题1——2022ctfshow卷王杯

task.py

Python
from Crypto.Util.number import bytes_to_long
from secrets import p,q,r,s,t,flag

n = p * q * r * s * t
e = 2
m = bytes_to_long(os.urandom(500) + flag)
c = pow(m,e,n)

print(p,q,r,s,t,sep='\n')
print(c)

'''
145332367700944303747548912160113939198078051436029477960348968315913956664143693347226702600438608693933768134575289286283267810723137895903153829001826223446477799895493265422562348917012216790077395795861238257357035152687833639085415763850743538206986781418939737511715957738982536382066693822159860701263
116660458253067608044065523310547233337730583902133756095473339390057738510707447906971188577217274861047379404014140178165569604404468897712846876108444468370709141219302291601408652742006268186059762087155933131837323952675627966299810891805398890428420575425160696531236660480933905879208166090591482794763
157931722402853245421436270609912823260313730941283152856444641969403238646482562190531038393124087232554754746464603598717356255570166081501573727336977292059427220330169044611674973569766966838498453232642731737958791706086957762244686953294662693939604300864961637325536379321027705854708492453330690705531
100973451687449518854742673778783266158999451072058606348222018797891147675959983616210003484476577612134482311993701677242007759556951494382833070563369964294544839433671087037596159753825249018950693369209927951667775267086896180395776150188902057785214767230658487267587289809918132337927575673868568976679
93960345071948255233882121683650797512129333868351496468898834736770441398743300745703393838320587998953678254272245400344928586394089488734271897540051673996675973642347859306921527430850673334243441180183460927865980713929789963587608547554858491264614271309608925634272282292964002897650355047792764365447
9144597920381774885442906257311149465702295057238600973973598305004391534618770363098565074541384771979931799878381439264848137810353858418200992191234142740194489573540381681161219332611454834544291634628456257670178843484698324641739324687497388018406214041657278323855749902661752448796122517061920880552011343608609622885787617238758769398972009949575526258430282648817039091284796330585349957724522615105102735930258969562103112238020133587096826386028128471852377225525357348919204333121695432662339443004327748973224423132988376298843862056631045488285859621661802413201793962883794915513510467912312842687601478117040419013468059983777273699192408773551806581458197324620065210523913467414181480875280203580147077789063808832356486197271376615883221558265591069223727607585313240243619515521180600435114131162272519949101464089935441251751426683447701142156416866113627126765919641034042927519834229168536331952275698122511502745177547569813354280565828372968703810158857859460406828090199683324760956105682902577189283246483314689365570862217407333103243336691401424548702387876409228977278498691200028282744239512091373110111792177228979867318546462714521296256938374618636206565791541769138267080789842400796973226733816939794717596194090232425688504890234304977612220790858557639246367437740975495450011676714198668471438814299689325208882261918460708833888406187912527346628912894921059735420931656953236560178909180587372589456926690219114173193202048332172538564489660440225377822914097420807957784201785024166011709377791129
'''

看似白给,其实\(e\)\(\phi(n)\)不互素

而且在flag前面填充了一些数据,导致\(m^e > n\),所以直接开根号也不可取

本题\(c \equiv m^e \mod n\),等价于m满足下面五个式子:

\[ m^2 \equiv c \mod p \]
\[ m^2 \equiv c \mod q \]
\[ m^2 \equiv c \mod r \]
\[ m^2 \equiv c \mod s \]
\[ m^2 \equiv c \mod t \]

我们在5个因子下求解,再用中国剩余定理组合即可

exp.sage

Python
from sympy.ntheory.modular import crt
import gmpy2
from Crypto.Util.number import *

p = 145332367700944303747548912160113939198078051436029477960348968315913956664143693347226702600438608693933768134575289286283267810723137895903153829001826223446477799895493265422562348917012216790077395795861238257357035152687833639085415763850743538206986781418939737511715957738982536382066693822159860701263
q = 116660458253067608044065523310547233337730583902133756095473339390057738510707447906971188577217274861047379404014140178165569604404468897712846876108444468370709141219302291601408652742006268186059762087155933131837323952675627966299810891805398890428420575425160696531236660480933905879208166090591482794763
r = 157931722402853245421436270609912823260313730941283152856444641969403238646482562190531038393124087232554754746464603598717356255570166081501573727336977292059427220330169044611674973569766966838498453232642731737958791706086957762244686953294662693939604300864961637325536379321027705854708492453330690705531
s = 100973451687449518854742673778783266158999451072058606348222018797891147675959983616210003484476577612134482311993701677242007759556951494382833070563369964294544839433671087037596159753825249018950693369209927951667775267086896180395776150188902057785214767230658487267587289809918132337927575673868568976679
t = 93960345071948255233882121683650797512129333868351496468898834736770441398743300745703393838320587998953678254272245400344928586394089488734271897540051673996675973642347859306921527430850673334243441180183460927865980713929789963587608547554858491264614271309608925634272282292964002897650355047792764365447
c = 9144597920381774885442906257311149465702295057238600973973598305004391534618770363098565074541384771979931799878381439264848137810353858418200992191234142740194489573540381681161219332611454834544291634628456257670178843484698324641739324687497388018406214041657278323855749902661752448796122517061920880552011343608609622885787617238758769398972009949575526258430282648817039091284796330585349957724522615105102735930258969562103112238020133587096826386028128471852377225525357348919204333121695432662339443004327748973224423132988376298843862056631045488285859621661802413201793962883794915513510467912312842687601478117040419013468059983777273699192408773551806581458197324620065210523913467414181480875280203580147077789063808832356486197271376615883221558265591069223727607585313240243619515521180600435114131162272519949101464089935441251751426683447701142156416866113627126765919641034042927519834229168536331952275698122511502745177547569813354280565828372968703810158857859460406828090199683324760956105682902577189283246483314689365570862217407333103243336691401424548702387876409228977278498691200028282744239512091373110111792177228979867318546462714521296256938374618636206565791541769138267080789842400796973226733816939794717596194090232425688504890234304977612220790858557639246367437740975495450011676714198668471438814299689325208882261918460708833888406187912527346628912894921059735420931656953236560178909180587372589456926690219114173193202048332172538564489660440225377822914097420807957784201785024166011709377791129
e = 2

R.<x> = PolynomialRing(Zmod(p))
f = x^e - c
f = f.monic()
res1 = f.roots()

R.<x> = PolynomialRing(Zmod(q))
f = x^e - c
f = f.monic()
res2 = f.roots()

R.<x> = PolynomialRing(Zmod(r))
f = x^e - c
f = f.monic()
res3 = f.roots()

R.<x> = PolynomialRing(Zmod(s))
f = x^e - c
f = f.monic()
res4 = f.roots()

R.<x> = PolynomialRing(Zmod(t))
f = x^e - c
f = f.monic()
res5 = f.roots()


for i in res1:
    for j in res2:
        for k in res3:
            for a in res4:
                for b in res5:
                    m_list = [int(i[0]),int(j[0]),int(k[0]),int(a[0]),int(b[0])]
                    a_list= [p,q,r,s,t]
                    solve = CRT_list(m_list,a_list)
                    flag = long_to_bytes(solve)
                    if b'ctfshow' in flag:
                        print(flag)
## ctfshow{D0_y0u_R3aLly_Kn0w_Ra8IN_alg0RI7HM?}

例题2——2018高校运维挑战赛AzureRSA

task.txt

Python
n1=0xcfc59d54b4b2e9ab1b5d90920ae88f430d39fee60d18dddbc623d15aae645e4e50db1c07a02d472b2eebb075a547618e1154a15b1657fbf66ed7e714d23ac70bdfba4c809bbb1e27687163cb09258a07ab2533568192e29a3b8e31a5de886050b28b3ed58e81952487714dd7ae012708db30eaf007620cdeb34f150836a4b723L
e1=0xfae3aL
c1=0x81523a330fb15125b6184e4461dadac7601340960840c5213b67a788c84aecfcdc3caf0bf3e27e4c95bb3c154db7055376981972b1565c22c100c47f3fa1dd2994e56090067b4e66f1c3905f9f780145cdf8d0fea88a45bae5113da37c8879c9cdb8ee9a55892bac3bae11fbbabcba0626163d0e2e12c04d99f4eeba5071cbeaL
n2=0xd45304b186dc82e40bd387afc831c32a4c7ba514a64ae051b62f483f27951065a6a04a030d285bdc1cb457b24c2f8701f574094d46d8de37b5a6d55356d1d368b89e16fa71b6603bd037c7f329a3096ce903937bb0c4f112a678c88fd5d84016f745b8281aea8fd5bcc28b68c293e4ef4a62a62e478a8b6cd46f3da73fa34c63L
e2=0x1f9eaeL
c2=0x4d7ceaadf5e662ab2e0149a8d18a4777b4cd4a7712ab825cf913206c325e6abb88954ebc37b2bda19aed16c5938ac43f43966e96a86913129e38c853ecd4ebc89e806f823ffb802e3ddef0ac6c5ba078d3983393a91cd7a1b59660d47d2045c03ff529c341f3ed994235a68c57f8195f75d61fc8cac37e936d9a6b75c4bd2347L
assert pow(flag,e1,n1)==c1
assert pow(flag,e2,n2)==c2
assert gcd(e1,(p1-1)*(q1-1))==14
assert gcd(e2,(p2-1)*(q2-1))==14

本题两个n均可分解

分解情况如下:

Python
p1 = 12037827528067911684278967221392433256129944002157200272548317200308481572950474775891360447285969682243206009995242277136633522897723532096467191105943909
q1 = 12120327527644543811107783655014863098833219936714394976342507913322405566177432644931575840816861543106701611662013978324877080018872656490603706071067111
p2 = 12120327527644543811107783655014863098833219936714394976342507913322405566177432644931575840816861543106701611662013978324877080018872656490603706071067111
q2 = 12301580698247665838432962460247894405698817646605188562297985838079356879336309758823376086396761749681729993573203954506346863479448531269351981555913253

我们可以发现\(n_1,n_2\)之间存在公因数

\(e\)\(\phi(n)\)的公因数为\(t\),则\(e = e't\)\(e' = \frac{e}{t}\),有\(e'd \equiv 1 \mod \phi(n)\)

\(\therefore m^t \equiv c^d \mod n\)

本题\(t = 14\),于是我们有

\[ res_1 \equiv m^{14} \mod n_1 \]
\[ res_2 \equiv m^{14} \mod n_2 \]

用中国剩余定理解出其特解\(res\),即\(res \equiv m^{14} \mod lcm(n_1,n_2) \longrightarrow res \equiv m^{14} \mod pq_1q_2\)

把它当作一个\(e=14\)的RSA加密,此时我们计算\(\phi(n) = (p_1 - 1)(q_1-1)(q_2-1)\)

我们分别求\(e = 14\)\(p_1 -1\),\(q_1-1\),\(q_2-1\)的公因数,结果分别是2,14,2

那么我们转换成\(res \equiv m^{14} \mod p_1q_2\)

我们分别在\(\mod p_1\)\(\mod q_2\)下用有限域开根求出解,再用中国剩余定理

exp.sage

Python
n1 = 0xcfc59d54b4b2e9ab1b5d90920ae88f430d39fee60d18dddbc623d15aae645e4e50db1c07a02d472b2eebb075a547618e1154a15b1657fbf66ed7e714d23ac70bdfba4c809bbb1e27687163cb09258a07ab2533568192e29a3b8e31a5de886050b28b3ed58e81952487714dd7ae012708db30eaf007620cdeb34f150836a4b723
e1 = 0xfae3a
c1 = 0x81523a330fb15125b6184e4461dadac7601340960840c5213b67a788c84aecfcdc3caf0bf3e27e4c95bb3c154db7055376981972b1565c22c100c47f3fa1dd2994e56090067b4e66f1c3905f9f780145cdf8d0fea88a45bae5113da37c8879c9cdb8ee9a55892bac3bae11fbbabcba0626163d0e2e12c04d99f4eeba5071cbea
n2 = 0xd45304b186dc82e40bd387afc831c32a4c7ba514a64ae051b62f483f27951065a6a04a030d285bdc1cb457b24c2f8701f574094d46d8de37b5a6d55356d1d368b89e16fa71b6603bd037c7f329a3096ce903937bb0c4f112a678c88fd5d84016f745b8281aea8fd5bcc28b68c293e4ef4a62a62e478a8b6cd46f3da73fa34c63
e2 = 0x1f9eae
c2 = 0x4d7ceaadf5e662ab2e0149a8d18a4777b4cd4a7712ab825cf913206c325e6abb88954ebc37b2bda19aed16c5938ac43f43966e96a86913129e38c853ecd4ebc89e806f823ffb802e3ddef0ac6c5ba078d3983393a91cd7a1b59660d47d2045c03ff529c341f3ed994235a68c57f8195f75d61fc8cac37e936d9a6b75c4bd2347

p1 = 12037827528067911684278967221392433256129944002157200272548317200308481572950474775891360447285969682243206009995242277136633522897723532096467191105943909
q1 = 12120327527644543811107783655014863098833219936714394976342507913322405566177432644931575840816861543106701611662013978324877080018872656490603706071067111
p2 = 12120327527644543811107783655014863098833219936714394976342507913322405566177432644931575840816861543106701611662013978324877080018872656490603706071067111
q2 = 12301580698247665838432962460247894405698817646605188562297985838079356879336309758823376086396761749681729993573203954506346863479448531269351981555913253


phi1 = (p1-1)*(q1-1)
phi2 = (p2-1)*(q2-1)
def decrypt(c,e,phi,n):
    t = gmpy2.gcd(e,phi)
    if t != 1:
        e1 = e // t
        d = gmpy2.invert(e1,phi)
        m = pow(c,d,n)
        return m

res1 = decrypt(c1,e1,phi1,n1)
res2 = decrypt(c2,e2,phi2,n2)


res = crt([res1,res2],[n1,n2])
phi = (p1 - 1)*(q2 - 1)
n = p1 * q2
m2 = decrypt(res,14,phi,n)

R.<x> = PolynomialRing(Zmod(p1))
f = x^2 - m2
sol1 = f.roots()

R.<x> = PolynomialRing(Zmod(q2))
f = x^2 - m2
sol2 = f.roots()

for i in sol1:
    for j in sol2:
        m = crt([int(i[0]),int(j[0])],[p1,q2])
        print(long_to_bytes(m))
        ## EIS{Comm0n_Div15or_plus_CRT_is_so_easy|cb2733b9e69ab3a9bd526fa1}

处理方式3:

AMM开根,具体原理可以参考:奇安信攻防社区-有限域上的高次开根AMM算法在RSA上的应用 (butian.net)

例题 2021黑盾杯Crypto1

task.txt

Python
e = 1801
c = pow(m,e,p)

c =821562155714228494350968286343241874202753771452745916900616612053610190986294297934462409534126095213198464996196364868528238538372119009517541428785632007137206972918081643841690069171088425923887930051635578719252415693144672179185417101210954906623326286804995637775062840407550493095027500638719998 
p =19897846550210846565807788524492364050901480736489979129040638436463635149815428186161001280958415730930156556581274966745574164608778242980049611665461488306439665507971670397595035647317930606555771720849158745264269952668944940061576328219674721623208805067371087817766416300084129945316973502412996143 

exp.sage

Python
from Crypto.Util.number import *
import gmpy2
import time
import random
from tqdm import tqdm

e = 1801
c = 821562155714228494350968286343241874202753771452745916900616612053610190986294297934462409534126095213198464996196364868528238538372119009517541428785632007137206972918081643841690069171088425923887930051635578719252415693144672179185417101210954906623326286804995637775062840407550493095027500638719998
p = 19897846550210846565807788524492364050901480736489979129040638436463635149815428186161001280958415730930156556581274966745574164608778242980049611665461488306439665507971670397595035647317930606555771720849158745264269952668944940061576328219674721623208805067371087817766416300084129945316973502412996143

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(a, d)
            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))
    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

mp = AMM(c,e,p)
p_proot = findAllPRoot(p, e)
mps = findAllSolutions(mp, p_proot, c, p)

for i in mps:
    flag = long_to_bytes(int(i))
    if b'flag' in flag:
        print(flag)
        ## flag{Enj01_m1sc_A0d_cr0}

另一种解法:

Python
from Crypto.Util.number import *

e = 1801
c = 821562155714228494350968286343241874202753771452745916900616612053610190986294297934462409534126095213198464996196364868528238538372119009517541428785632007137206972918081643841690069171088425923887930051635578719252415693144672179185417101210954906623326286804995637775062840407550493095027500638719998
p = 19897846550210846565807788524492364050901480736489979129040638436463635149815428186161001280958415730930156556581274966745574164608778242980049611665461488306439665507971670397595035647317930606555771720849158745264269952668944940061576328219674721623208805067371087817766416300084129945316973502412996143

g = pow(2,(p-1) // e,p)
d0 = inverse(e,(p - 1) // e)
m0 = pow(c,d0,p)

for i in range(e):
    m = m0 * pow(g,i,p) % p
    flag = long_to_bytes(m)
    if b"flag" in flag:
        print(flag)

没搞懂原理

十三、Rabin

简单了解一下Rabin加密体制流程

一年后回头再看,Rabin只是e=2的RSA

密钥生成

选取两个大素数\(p,q\),并且满足

\[ p \equiv 3 \mod 4 \]
\[ q \equiv 3 \mod 4 \]

\(p,q\)都是\(4k + 3\)形式的素数

接下来计算\(n = p\times q\)\((p,q)\)作为私钥,\(n\)作为公钥

加密过程

\[ c \equiv m^2(mod \quad n) \]

解密过程

\[ \because c \equiv m^2 \mod n \]

\[ c \equiv m ^ 2 \mod p \]
\[ c \equiv m ^ 2 \mod q \]

根据二次剩余得

\(c^{\frac{p-1}{2}} \equiv 1 \mod p\)同乘c得:\(c^{\frac{p+1}{2}} \equiv c \mod p\),开根号得\(c^{\frac{p+1}{4}} \mod p\),所以\(m_p =c^{\frac{p+1}{4}}\)

同理\(m_q = c^{\frac{q+1}{4}}\)

此时得到两个同余式

\[ m \equiv m_p \mod p \]
\[ m \equiv m_q \mod q \]

再解中国剩余定理即可

例题

task.py

Python
import gmpy2
import libnum
import random
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)
p = libnum.generate_prime(512)
q = libnum.generate_prime(512)
n = p * q
e = 2
c = pow(m, e, n)
print("p=", p)
print("q=", q)
print("n=", n)
print("c=", c)
print("e=", e)

exp.py

Python
import gmpy2
from Crypto.Util.number import *

p= 13314362720917602133969793252481444316247612541287913579795797774897851142465370812511985605994998073433561235021924708067874236781706611485522371488232623
q= 10711516497864529822020903304369858958930042711451857859791558135232705853374332295320094676061294774680048276363750484537945067473851592618816785752809803
n= 142617015943661365869136488949628046624950745436416195175536421279851751774402291519010668496730147866462378904972514253007029491632653461771497495733152729212900224242492221133006321373540619376544526943204146111719397605460529531608654329571698129077171108416638061409327725762114841023226929126272738803269
c= 3136716033731857617369889733308430982192049734478834150612954276064433287574994581343168243135342097712334250357890492021703404008556737899236668582163912130637742227325170121650071435166332685070232329
e= 2

inv_p = gmpy2.invert(p, q)
inv_q = gmpy2.invert(q, p)
mp = pow(c, (p + 1) // 4, p)
mq = pow(c, (q + 1) // 4, q)
a = (inv_p * p * mq + inv_q * q * mp) % n
b = n - int(a)
c = (inv_p * p * mq - inv_q * q * mp) % n
d = n - int(c)
## 因为rabin 加密有四种结果,全部列出。
aa = [a, b, c, d]
for i in aa:
    print(long_to_bytes(int(i)))

两次有限域开根再中国剩余定理也可以解

exp.sage

Python
import gmpy2
from Crypto.Util.number import *

p = 13314362720917602133969793252481444316247612541287913579795797774897851142465370812511985605994998073433561235021924708067874236781706611485522371488232623
q = 10711516497864529822020903304369858958930042711451857859791558135232705853374332295320094676061294774680048276363750484537945067473851592618816785752809803
n = 142617015943661365869136488949628046624950745436416195175536421279851751774402291519010668496730147866462378904972514253007029491632653461771497495733152729212900224242492221133006321373540619376544526943204146111719397605460529531608654329571698129077171108416638061409327725762114841023226929126272738803269
c = 3136716033731857617369889733308430982192049734478834150612954276064433287574994581343168243135342097712334250357890492021703404008556737899236668582163912130637742227325170121650071435166332685070232329
e = 2

R.<x> = PolynomialRing(Zmod(p))
f = x^2 - c
res1 = f.roots()

R.<x> = PolynomialRing(Zmod(q))
f = x^2 - c
res2 = f.roots()

for i in res1:
    for j in res2:
        m = crt([int(i[0]),int(j[0])],[p,q])
        print(long_to_bytes(int(m)))

多次解Rabin

题目来源NSSCTF ROUND#11

Python
from Crypto.Util.number import *
from secret import flag

p = getPrime(512)
q = getPrime(512)
assert p > q
n = p*q
e = 65536
m = bytes_to_long(flag)
num1 = (pow(p,e,n)-pow(q,e,n)) % n
num2 = pow(p-q,e,n)
c = pow(m,e,n)

print("num1=",num1)
print("num2=",num2)
print("n=",n)
print("c=",c)

先当脚本小子

exp.py

Python
from Crypto.Util.number import *
import gmpy2

num1= 134186458247304184975418956047750205959249518467116558944535042073046353646812210914711656218265319503240074967140027248278994209294869476247136854741631971975560846483033205230015783696055443897579440474585892990793595602095853960468928457703619205343030230201261058516219352855127626321847429189498666288452
num2= 142252615203395148320392930915384149783801592719030740337592034613073131106036364733480644482188684184951026866672011061092572389846929838149296357261088256882232316029199097203257003822750826537629358422813658558008420810100860520289261141533787464661186681371090873356089237613080052677646446751824502044253
n= 154128165952806886790805410291540694477027958542517309121222164274741570806324940112942356615458298064007096476638232940977238598879453357856259085001745763666030177657087772721079761302637352680091939676709372354103177660093164629417313468356185431895723026835950366030712541994019375251534778666996491342313
c= 9061020000447780498751583220055526057707259079063266050917693522289697419950637286020502996753375864826169562714946009146452528404466989211057548905704856329650955828939737304126685040898740775635547039660982064419976700425595503919207903099686497044429265908046033565745195837408532764433870408185128447965

tmp = num1+num2

p = gmpy2.gcd(n,tmp)
q = n // p

inv_p = gmpy2.invert(p, q)
inv_q = gmpy2.invert(q, p)

cs = [c]
for i in range(16):
    ps = []
    for c2 in cs:
        r = pow(c2, (p + 1) // 4, p)
        s = pow(c2, (q + 1) // 4, q)

        x = (r * inv_q * q + s * inv_p * p) % n
        y = (r * inv_q * q - s * inv_p * p) % n
        if x not in ps:
            ps.append(x)
        if n - x not in ps:
            ps.append(n - x)
        if y not in ps:
            ps.append(y)
        if n - y not in ps:
            ps.append(n - y)
    cs = ps

for m in cs:
    print(long_to_bytes(m))

下面的三个题型将涉及CopperSmith,师傅们可以通读一遍:密码学学习笔记 之 Coppersmith’s Method | Van1sh的小屋 (jayxv.github.io)

十四、m高位泄露

这类题目会有两种情况,第一种情况是代码很直白的给出m的高位,第二种情况是利用flag头之类的信息来获取高位

情况1:直接给出m的高位

例题

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"

m = libnum.s2n(flag)
p = libnum.generate_prime(512)
q = libnum.generate_prime(512)
n = p * q
m1 = ((m >> 300) << 300)
e = 3
c = pow(m, e, n)

print("n =", n)
print("c =", c)
print("e =", e)
print("m1 =", m1)
"""
n = 89449659165373664419599666603079125351786161891254199117045952387178298091726343680198833028457552478360463001966084179577576990766724383528486250298768666097882661565765202949881673799125950008229527772973603379916117935790703989917655837756217393025354969853495238961762669326690100145696530290685977278593
c = 175676150266637032446485438802315236532165610668245377791442524569846934587347877260997089793552024464242407355427955652050327122630649033488529560135042402737218074153101020540979958586399761415542047850951996733997549252310811350769808163958303048261417309757780500291307110596781699418188514782424677
e = 3
m1 = 56006392791978632601307849398736624069259692797382051345599538506145258779412417900757721790977933312
"""

exp.sage

Python
from Crypto.Util.number import long_to_bytes

n = 89449659165373664419599666603079125351786161891254199117045952387178298091726343680198833028457552478360463001966084179577576990766724383528486250298768666097882661565765202949881673799125950008229527772973603379916117935790703989917655837756217393025354969853495238961762669326690100145696530290685977278593
c = 175676150266637032446485438802315236532165610668245377791442524569846934587347877260997089793552024464242407355427955652050327122630649033488529560135042402737218074153101020540979958586399761415542047850951996733997549252310811350769808163958303048261417309757780500291307110596781699418188514782424677
e = 3
m1 = 56006392791978632601307849398736624069259692797382051345599538506145258779412417900757721790977933312


R.<x> = PolynomialRing(Zmod(n))
f = (m1 + x)^e - c
res = f.small_roots(X = 2^300,beta = 1)
if res != []:
    m = m1 + res[0]
    print(long_to_bytes(int(m)))

情况2:利用flag头等已知信息

例题——2024XYCTF 反方向的密码 相思

task.py

Python
from Crypto.Util.number import *
import hashlib
from secrets import flag

def hash(x):
    return hashlib.sha256(x.encode()).digest()

def pad(message):
    return message + hash(str(len(message)))

m = bytes_to_long(pad(flag))
p = getStrongPrime(512)
q = getStrongPrime(512)
n = p * q
e = 3
print(pow(m, e, n))
print(n)
## 120440199294949712392334113337541924034371176306546446428347114627162894108760435789068328282135879182130546564535108930827440004987170619301799710272329673259390065147556073101312748104743572369383346039000998822862286001416166288971531241789864076857299162050026949096919395896174243383291126202796610039053
## 143413213355903851638663645270518081058249439863120739973910994223793329606595495141951165221740599158773181585002460087410975579141155680671886930801733174300593785562287068287654547100320094291092508723488470015821072834947151827362715749438612812148855627557719115676595686347541785037035334177162406305243

m可以看为m = bytes_to_long(b"XYCTF{xxxxxxxxxxxxxxxxxxxx}" + pad)

pad可以爆破,所以只需要求解中间未知的字符,就相当于一个m高位和低位泄露的题

exp.sage

Python
from Crypto.Util.number import *
import hashlib

c = 120440199294949712392334113337541924034371176306546446428347114627162894108760435789068328282135879182130546564535108930827440004987170619301799710272329673259390065147556073101312748104743572369383346039000998822862286001416166288971531241789864076857299162050026949096919395896174243383291126202796610039053
n = 143413213355903851638663645270518081058249439863120739973910994223793329606595495141951165221740599158773181585002460087410975579141155680671886930801733174300593785562287068287654547100320094291092508723488470015821072834947151827362715749438612812148855627557719115676595686347541785037035334177162406305243

def hash(x):
    return hashlib.sha256(x.encode()).digest()

for i in range(10,40):                                  #i代表{}中未知数的个数
    prefix = bytes_to_long(b"XYCTF{") * 256^(32 + 1 + i)

    pad = hash(str(i+7))
    low = bytes_to_long(b"}" + pad)

    R.<x> = PolynomialRing(Zmod(n))

    f = (prefix + x*256^33 + low)^3 - c
    f = f.monic()
    res = f.small_roots(X=256^i)
    if res != []:
        m = prefix + int(res[0])*256^33 + low
        print(long_to_bytes(int(m)))
        ## XYCTF{!__d3ng__hu0__1@n__3h@n__Chu__!}

十五、p高位泄露

这类题目也有两种情况,第一种情况是代码很直白的给出p的高位,第二种情况是利用其他信息获取p的高位

p的高位泄露是有条件的,经过我的不完全准确的统计(以512bit的p为例)

beta=0.4时,在未知位数少于等于227bit时,可以恢复p

beta=0.4,epsilon=0.01时,在未知位数少于等于248bit时,可以恢复p

例题1

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n = p * q
e = 65537
p1 = ((p >> 256) << 256)
c = pow(m, e, n)
print("n =", n)
print("c =", c)
print("e =", e)
print("p1 =", p1)
"""
n = 25412529168113241701978154909309452589891277619233574149177682749114694998754065178357796120946044207438029964927062297462478639487054245771692139625849995546462111249773750389956600652870714030563605023702161513442866609937847422556059608499864578170270126239637444959812121589196901129645564580908045557218006330672260617798453633543627607790892960895904087738679492228779277752739734608284915605638138243297198792777112179909034121738317943791638151509031222745851940804609153731748776002638455252820409482251897734211263626398933847861538578761515233197699408866935293343456161438639398890089434015875977218250399
c = 11832017536820850822990089557724269863899815227776358234936663617587241671432348464674233033185082415100398087268764691266148966050993867487703853445199053612975005945065305992972854436834255574224203980327515867976652513642869374642661621841855735886358661741631500029948344306194937625466742946921106659582316643893949403548828556819907948714983462986507817955376793071143086032085845182853003634916425312911434933416448638808160541352834497686975061092257675653110414264672490891200433992043618040530275350634723043175666891682239033429796170154980192829798607191555482341170827932551423162833473892514972387547888
e = 65537
p1 = 174553077493661081676268351785469032783476528521014320107330821253075206651723812600343204348306643031578070226902979453793372875180300634043015175074746912397887970195415967163417566827459210112867899640388623940244194079258961165834045219906954836822089775153665401751146892024327963835455414059781203165184
"""

exp.sage

Python
import gmpy2
from Crypto.Util.number import *

n = 25412529168113241701978154909309452589891277619233574149177682749114694998754065178357796120946044207438029964927062297462478639487054245771692139625849995546462111249773750389956600652870714030563605023702161513442866609937847422556059608499864578170270126239637444959812121589196901129645564580908045557218006330672260617798453633543627607790892960895904087738679492228779277752739734608284915605638138243297198792777112179909034121738317943791638151509031222745851940804609153731748776002638455252820409482251897734211263626398933847861538578761515233197699408866935293343456161438639398890089434015875977218250399
c = 11832017536820850822990089557724269863899815227776358234936663617587241671432348464674233033185082415100398087268764691266148966050993867487703853445199053612975005945065305992972854436834255574224203980327515867976652513642869374642661621841855735886358661741631500029948344306194937625466742946921106659582316643893949403548828556819907948714983462986507817955376793071143086032085845182853003634916425312911434933416448638808160541352834497686975061092257675653110414264672490891200433992043618040530275350634723043175666891682239033429796170154980192829798607191555482341170827932551423162833473892514972387547888
e = 65537
p_high = 174553077493661081676268351785469032783476528521014320107330821253075206651723812600343204348306643031578070226902979453793372875180300634043015175074746912397887970195415967163417566827459210112867899640388623940244194079258961165834045219906954836822089775153665401751146892024327963835455414059781203165184

R.<x> = PolynomialRing(Zmod(n))
f = p_high + x
res = f.small_roots(X = 2^256,beta = 0.4)
if res != []:
    p = p_high + int(res[0])
    q = n // p
    d = gmpy2.invert(e,(p-1)*(q-1))
    m = pow(c,d,n)
    print(long_to_bytes(int(m)))

例题2——2023LitCTF Babyxor

task.py

Python
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)
assert len(flag)==32
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537
c1 = p^m
c2 = pow(m,e,n) 
print(f'n = {n}')
print(f'c1 = {c1}')
print(f'c2 = {c2}')
"""
n = 139167681803392690594490403105432649693546256181767408269202101512534988406137879788255103631885736461742577594980136624933914700779445704490217419248411578290305101891222576080645870988658334799437317221565839991979543660824098367011942169305111105129234902517835649895908656770416774539906212596072334423407
c1 = 11201139662236758800406931253538295757259990870588609533820056210585752522925690049252488581929717556881067021381940083808024384402885422258545946243513996
c2 = 112016152270171196606652761990170033221036025260883289104273504703557624964071464062375228351458191745141525003775876044271210498526920529385038130932141551598616579917681815276713386113932345056134302042399379895915706991873687943357627747262597883603999621939794450743982662393955266685255577026078256473601
"""

因为flag长度为32,转为int的值为256bit,因为p为512的数,所以p ^ m的高256bit和p的高256bit是一样的,间接告诉我们p的高位

这个未知位数不太满足我们前面说的攻击条件,差了8位,因此需要做一点爆破

exp.sage

Python
import gmpy2
from tqdm import *
from Crypto.Util.number import *

n = 139167681803392690594490403105432649693546256181767408269202101512534988406137879788255103631885736461742577594980136624933914700779445704490217419248411578290305101891222576080645870988658334799437317221565839991979543660824098367011942169305111105129234902517835649895908656770416774539906212596072334423407
c1 = 11201139662236758800406931253538295757259990870588609533820056210585752522925690049252488581929717556881067021381940083808024384402885422258545946243513996
c2 = 112016152270171196606652761990170033221036025260883289104273504703557624964071464062375228351458191745141525003775876044271210498526920529385038130932141551598616579917681815276713386113932345056134302042399379895915706991873687943357627747262597883603999621939794450743982662393955266685255577026078256473601
e = 65537
pbits = 512

p_high = c1 >> 256
for i in trange(2**8):
    p4 = p_high << 8            #这里需要先爆破8位,使得知道264位以后再恢复p
    p4 = p4 + i
    kbits = pbits - p4.nbits()
    p4 = p4 << kbits
    R.<x> = PolynomialRing(Zmod(n))
    f = x + p4
    res = f.small_roots(X=2^kbits, beta=0.4, epsilon=0.01)
    if res != []:
        p = p4 + int(x[0])
        q = n // p
        d = gmpy2.invert(e,(p-1)*(q-1))
        m = pow(c2,d,n)
        print(long_to_bytes(int(m)))
        break

#LitCTF{oh!!!!coppersmith_is_fun}

例题3

有些题目不一定给出高位,还有可能给出低位,甚至给出部分高位和部分低位。其本质是一样的,我们只需要改变多项式即可

task.py

Python
from secret import flag
from Crypto.Util.number import *


p, q = getPrime(1024), getPrime(1024)
N = p * q
p0 = p ^ (bytes_to_long(flag)<<444)     
m = bytes_to_long(flag)
c = pow(m, 65537, N)
print(len(flag))
print('c=',c)
print('N=',N)
print('p0=',p0)


## 54   
## c= 6187845844052645335477666563187579997555293403110620879123121166924703361821847984760733842049752886698011561451715570810292323756091403783920480396052120046379755571530451812078574368413924390017994278703794257118954968480994077586245800902748815905644287545189605031883291488844527496906890127546594960138582150272568163575590734246290813150131949296550974206595456421136190026954855755623761557179760444906148376433584795779131477110538212742401420633087881506416368853221110426491868881029814841479615979710066371796507692025126150957315754738584387325388998533227577023142894876376702128870643448600352603905149
## N= 14195810221536708489210274086946022255792382922322850338983263099316341767896809249586174293795778082892237356582757544364274847341220303582304283372889068290282580493623042441421715338444710303281638639785784613434328659529884972238348336971186339482788748316527376410510261228354155806341136524162787121212184386900663470590652770503564816948407257603737938414126069053610568675347826390537145556511048774030823322301932088595499671755944744816524811272617200683384649389274196659297432212847319503330409792704612575414010711158873031786577877685578976140462539734553598745329712188216200905451774357282278403189943
## p0= 111984935426070810628244029907949697819351004665202173622240566580193974673163315128983603277856218378729883402496424467491406698035050254407170555432448523469880166015507303737468316933545613178461925283040643344197452758878116752692499745309765526523083790825015522124083482964296662782850606081657447935191

因为m左移444位,这样子m后面444位都是0,所以异或之后,不影响p低位444值

根据flag长度是54,转成int数据就432bit,左移444位后是876位,异或之后,p的高(1024 - 876) = 148位也是不变的。所以我们有了p的部分高位和低位

还是利用coppersmith,改个多项式即可

exp.sage

Python
import gmpy2
from Crypto.Util.number import *

p0 = 111984935426070810628244029907949697819351004665202173622240566580193974673163315128983603277856218378729883402496424467491406698035050254407170555432448523469880166015507303737468316933545613178461925283040643344197452758878116752692499745309765526523083790825015522124083482964296662782850606081657447935191
c = 6187845844052645335477666563187579997555293403110620879123121166924703361821847984760733842049752886698011561451715570810292323756091403783920480396052120046379755571530451812078574368413924390017994278703794257118954968480994077586245800902748815905644287545189605031883291488844527496906890127546594960138582150272568163575590734246290813150131949296550974206595456421136190026954855755623761557179760444906148376433584795779131477110538212742401420633087881506416368853221110426491868881029814841479615979710066371796507692025126150957315754738584387325388998533227577023142894876376702128870643448600352603905149
n = 14195810221536708489210274086946022255792382922322850338983263099316341767896809249586174293795778082892237356582757544364274847341220303582304283372889068290282580493623042441421715338444710303281638639785784613434328659529884972238348336971186339482788748316527376410510261228354155806341136524162787121212184386900663470590652770503564816948407257603737938414126069053610568675347826390537145556511048774030823322301932088595499671755944744816524811272617200683384649389274196659297432212847319503330409792704612575414010711158873031786577877685578976140462539734553598745329712188216200905451774357282278403189943
e = 65537

phigh = p0 >> 876 <<876
tmp = int("1" * 444,2)
plow = p0 & tmp

R.<x> = PolynomialRing(Zmod(n))

f = phigh + x*2**444 + plow
f = f.monic()
res = f.small_roots(X=2^432,beta=0.4)
if res != []:
    p = int(phigh + res[0]*2**444+plow)
    print("p =",p)
    q = n // p
    d = gmpy2.invert(e,(p-1)*(q-1))
    m = pow(c,d,n)
    print(long_to_bytes(int(m)))

十六、d低位泄露

攻击条件:e在可爆破范围,并且已知d的低位

推导:

\[ \because ed \equiv 1 \mod \phi(n) \]
\[ \therefore ed = k(p-1)(q-1) + 1 \]

\[ ed = kn - kp - kq + k + 1 \]

假设我们知道d的低L位,并且记为\(d_{low}\),我们对上式同模\(2^L\),于是有

\[ ed_{low} \equiv kn - kp - kq + k + 1 \mod 2^L \]

两边同时乘上p得到

\[ ed_{low} \equiv knp -kp^2 - kn +(k+1)p \mod 2^L \]

此时上式只有k和p是未知的,因为\(0 < k < e\),所以可对k进行爆破,解同余式得到p的低位,再用coppersmith恢复p

例题

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
while True:
    p = libnum.generate_prime(512)
    q = libnum.generate_prime(512)
    n = p * q
    phi_n = (p - 1) * (q - 1)
    e = 3
    if gmpy2.gcd(e, phi_n) == 1:
        break

d = gmpy2.invert(e, phi_n)
d1 = d & ((1 << 486) - 1)
c = pow(m, e, n)
print("n =", n)
print("e =", e)
print("c =", c)
print("dlow =", d1)
"""
n = 52742121495363456830645578439066440260525635701449452038623040460534514676940962601979528301396249463310635013618692939847284377316096998174539886900202801371512846078230196507601167693580439935790730812348102969592553942497201279143623565409782692807615767067494338127711608771029145341503069929345973282553
e = 3
c = 175676150266403096249431407570554929021369604168136874268275844375181688894753012319267533769923727863698572157250017689244151605424698000831986135513112136594197978237874400825236288135820643994889528170234514127521418132167932446836545721803621236277931291609749528037915073270452302613264065194049125
dlow = 8681746171122274095992828774600905650260279086049883317863420271848455130773826835383939352151607850521826888046119078445911475580784484455485323
"""

exp.sage

Python
from Crypto.Util.number import long_to_bytes,inverse
from tqdm import *

n = 52742121495363456830645578439066440260525635701449452038623040460534514676940962601979528301396249463310635013618692939847284377316096998174539886900202801371512846078230196507601167693580439935790730812348102969592553942497201279143623565409782692807615767067494338127711608771029145341503069929345973282553
e = 3
c = 175676150266403096249431407570554929021369604168136874268275844375181688894753012319267533769923727863698572157250017689244151605424698000831986135513112136594197978237874400825236288135820643994889528170234514127521418132167932446836545721803621236277931291609749528037915073270452302613264065194049125
dlow = 8681746171122274095992828774600905650260279086049883317863420271848455130773826835383939352151607850521826888046119078445911475580784484455485323

def get_full_p(p_low,n,pbits):
    kbits = p_low.bit_length()
    R.<x> = PolynomialRing(Zmod(n))
    f = x * 2^kbits + p_low
    f = f.monic()
    res = f.small_roots(X = 2^(pbits-kbits),beta=0.4)
    if res != []:
        p = int(res[0]) * 2^kbits + p_low
        return p

for k in trange(e):
    var('p')
    f1 = e*dlow*p - (k*n*p - k*p^2 - k*n + (k+1)*p)
    roots = solve_mod(f1,2^486)
    if roots != []:
        for root in roots:
            if int(root[0]).bit_length() == 486:
                p = get_full_p(int(root[0]),n,512)
                if p:
                    q = n // p
                    d = inverse(3,(p-1)*(q-1))
                    m = pow(c,d,n)
                    print(long_to_bytes(m))
                    break

2023强网拟态决赛——BadRSA

task.py

Python
from Crypto.Util.number import *

f = open('flag.txt','rb')
m = bytes_to_long(f.readline().strip())

p = getPrime(512)
q = getPrime(512)
e = getPrime(8)
n = p*q
phi = (p-1)*(q-1)
d = inverse(e,phi)
leak = d & ((1<<265) - 1)

print(f'e = {e}')
print(f'leak = {leak}')
print(f'n = {n}')
c = pow(m,e,n)
print(f'c = {c}')

'''
e = 149
leak = 6001958312144157007304943931113872134090201010357773442954181100786589106572169
n = 88436063749749362190546240596734626745594171540325086418270903156390958817492063940459108934841028734921718351342152598224670602551497995639921650979296052943953491639892805985538785565357751736799561653032725751622522198746331856539251721033316195306373318196300612386897339425222615697620795751869751705629
c = 1206332017436789083799133504302957634035030644841294494992108068042941783794804420630684301945366528832108224264145563741764232409333108261614056154508904583078897178425071831580459193200987943565099780857889864631160747321901113496943710282498262354984634755751858251005566753245882185271726628553508627299
'''

\(ed \equiv 1 \mod \phi(n)\)

\(ed = k(p-1)(q-1) + 1\)

两边同时模\(2^{265}\),得\(ed_0 \equiv k(p-1)(q-1) + 1 \mod 2^{265}\),这里\(d_0 = leak\)

\(\therefore ed_0 \equiv k(pq - p - q + 1) + 1 \mod 2^{265}\)

再同乘\(p\),得到\(ed_0p \equiv knp - kp^2 - kn + kp + p \mod 2^{265}\)

因为\(d \approx 1023bit\)

所以\(k < e\)

爆破\(k\),解方程得到\(p\)的低位,然后用copper恢复\(p\)

要调copper的参数,解方程得到的只有\(p\)的低265位,还差247位

exp.sage

Python
from Crypto.Util.number import *
import gmpy2

e = 149
d0 = 6001958312144157007304943931113872134090201010357773442954181100786589106572169
n = 88436063749749362190546240596734626745594171540325086418270903156390958817492063940459108934841028734921718351342152598224670602551497995639921650979296052943953491639892805985538785565357751736799561653032725751622522198746331856539251721033316195306373318196300612386897339425222615697620795751869751705629
c = 1206332017436789083799133504302957634035030644841294494992108068042941783794804420630684301945366528832108224264145563741764232409333108261614056154508904583078897178425071831580459193200987943565099780857889864631160747321901113496943710282498262354984634755751858251005566753245882185271726628553508627299

for k in range(1,e):
    var("p")
    temp = e*d0*p
    f = (k*n*p - k*p^2 - k*n + k*p + p) - temp  == 0

    roots = solve_mod([f],2^265)
    if roots != []:
        for root in roots:
            plow = int(root[0])
##             print(plow)
## 55136429770900518182274612434328885021714880080534773062619965935822096183916139
            R.<x> = PolynomialRing(Zmod(n))

            f1 = x*2^265 + plow
            f1 = f1.monic()
            res = f1.small_roots(X=2^247,beta=0.5,epsilon = 0.01)
            if res != []:
                print(res)
                ## [188210689227294472160085325314952069542671020803828390144430392548173787275]
                p = int(res[0])*2^265 + plow
                print("p =",p)
                ## p = 11158174168280917736979570452068827611755694573672250873587467083259280584739528118050085070475912733864211083865201596017044398008278425498714490994488939
                q = n // p
                d = gmpy2.invert(e,(p-1)*(q-1))
                m = pow(c,d,n)
                print(long_to_bytes(int(m)))
                ## flag{827ccb0eea8a706c4c34a16891f84e7b}
                break

这里beta = 0.5是因为,对于度(即最大次方)为\(d\)的多项式,要求根小于\(n^{\frac{\beta^2}{d} - \epsilon}\),而且要求因子\(p > n^{\beta}\)

此题\(d = 1\),经过计算我们要求的根的上界为\(2^{247}\)\(\frac{247}{1024} = 0.2412\),所以取beta = 0.5可以恢复p。界这个东西感觉有点玄学,只能多做题多感受了

十七、Franklin-Reiter相关消息攻击

攻击条件:

当Alice使用同一公钥对两个具有线性关系的消息\(M_1,M_2\)进行加密,并将加密后的消息\(C_1,C_2\)发送给了Bob,我们就可能可以获得对应的消息\(M_1,M_2\)。假设模数为\(N\)\(M_1,M_2\)的线性关系如下:

\(M_1 \equiv f(M_2) \mod N\)

其中\(f\)为一个线性函数,比如\(f = ax+b\)

存脚本就好了

例题1

task.py

Python
from secret import flag
from Crypto.Util.number import *

m1 = bytes_to_long(flag)
N = getPrime(512)*getPrime(512)
e = 17

c1 = pow(m1, e, N)

a = getRandomNBitInteger(512)
b = getRandomNBitInteger(512)
m2 = a * m1 + b
c2 = pow(m2, e, N)

print(N, a, b, c1, c2, sep="\n")

## 51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
## 13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
## 12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
## 18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
## 14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720

exp.sage

Python
import libnum

n = 51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
a = 13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
b = 12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
c1 = 18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
c2 = 14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720
e = 17

def franklinReiter(n,e,c1,c2,a,b):
    PR.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (a*x+b)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic() 
    return -gcd(g1, g2)[0]

m = franklinReiter(n,e,c1,c2,a,b)
print(libnum.n2s(int(m)))

例题2——SICTFround2 签到题来咯

task.py

Python
from secret import flag
from  Crypto.Util.number import *

m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
e = getPrime(10)
n = p*q
c1 = pow(114*m+2333,e,n)
c2 = pow(514*m+4555,e,n)
print(f'n = {n}')
print(f'c1 = {c1}')
print(f'c2 = {c2}')
'''
n = 18993579800590288733556762316465854395650778003397512624355925069287661487515652428099677335464809283955351330659278915073219733930542167360381688856732762552737791137784222098296804826261681852699742456526979985201331982720936091963830799430264680941164508709453794113576607749669278887105809727027129736803614327631979056934906547015919204770702496676692691248702461766117271815398943842909579917102217310779431999448597899109808086655029624478062317317442297276087073653945439820988375066353157221370129064423613949039895822016206336117081475698987326594199181180346821431242733826487765566154350269651592993856883
c1 = 3089900890429368903963127778258893993015616003863275300568951378177309984878857933740319974151823410060583527905656182419531008417050246901514691111335764182779077027419410717272164998075313101695833565450587029584857433998627248705518025411896438130004108810308599666206694770859843696952378804678690327442746359836105117371144846629293505396610982407985241783168161504309420302314102538231774470927864959064261347913286659384383565379900391857812482728653358741387072374314243068833590379370244368317200796927931678203916569721211768082289529948017340699194622234734381555103898784827642197721866114583358940604520
c2 = 6062491672599671503583327431533992487890060173533816222838721749216161789662841049274959778509684968479022417053571624473283543736981267659104310293237792925201009775193492423025040929132360886500863823523629213703533794348606076463773478200331006341206053010168741302440409050344170767489936681627020501853981450212305108039373119567034948781143698613084550376070802084805644270376620484786155554275798939105737707005991882264123315436368611647275530607811665999620394422672764116158492214128572456571553281799359243174598812137554860109807481900330449364878168308833006964726761878461761560543284533578701661413931
'''

本题未知e,爆破一下e即可

exp.sage

Python
## sage
import libnum
from Crypto.Util.number import *

n = 
c1 = 
c2 = 
E = []

for i in range(2^9,2^10):
    if isPrime(i):
        E.append(i)

def franklinReiter(n,e,c1,c2):
    PR.<x> = PolynomialRing(Zmod(n))
    g1 = (114*x + 2333)^e - c1
    g2 = (514*x + 4555)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()
    return -gcd(g1, g2)[0]

## print(E)
for e in E:
    m = franklinReiter(n,e,c1,c2)
    flag = long_to_bytes(int(m))
    if b'SICTF' in flag:
        print(flag)
        break
    else:
        continue

例题3——2023领航杯 RSA3

在前面的例子中,加密指数都不太大,脚本运行起来都算快。接下来这道例题,加密指数e为65537,如果不使用更优的方法,会很浪费时间

task.py

Python
from Crypto.Util.number import *
from secret import flag
m = bytes_to_long(flag)
p1, q1 = getPrime(512), getPrime(512)
n1 = p1*q1
e = 65537

p2, q2 = getPrime(512), getPrime(512)
n2 = p2*q2

print(f'n1 = {n1}')
print(f'n2 = {n2}')
print(f'c1 = {pow(m,e,n2)}')
print(f'c2 = {pow(n1-m,e,n2)}')

## n1 = 52579135273678950581073020233998071974221658902576724000130040488018033110534210901239397446395736563148970863970460542205225993317478251099451639165369081820130823165642873594136020122857712288395352930384057524510346112486008850200845915783772351449146183974239444691330777565342525218070680067550270554767
## n2 = 68210568831848267339414957973218186686176324296418282565773310695862151827108036984694027795077376921170907068110296451176263520249799154781062517066423984526868547296781709439425857993705489037768605485740968600877866332458671029054092942851472208033494968784822459369206497698469167909174346042658361616469
## c1 = 42941712708129054668823891960764339394032538100909746015733801598044118605733969558717842106784388091495719003761324737091667431446354282990525549196642753967283958283202592037329821712755519455155110675327321252333824912095517427885925854391047828862338332559137577789387455868761466777370476884779752953853
## c2 = 62704043252861638895370674827559804184650708692227789532879941590038911799857232898692335429773480889624046167792573885125945511356456073688435911975161053231589019934427151230924004944847291434167067905803180207183209888082275583120633408232749119300200555327883719466349164062163459300518993952046873724005

参考:HALF-GCD算法的阐述_half gcd_Entropy Increaser的博客-CSDN博客

多项式 gcd 的正确姿势:Half-GCD 算法 - whx1003 - 博客园 (cnblogs.com)

脚本来源:rkm0959_implements/Half_GCD/code.sage at main · rkm0959/rkm0959_implements (github.com)

我们引入使用了HGCD算法的Franklin-Reiter相关消息攻击的脚本

exp.sage

Python
from Crypto.Util.number import *
import sys

def HGCD(a, b):
    if 2 * b.degree() <= a.degree() or a.degree() == 1:
        return 1, 0, 0, 1
    m = a.degree() // 2
    a_top, a_bot = a.quo_rem(x^m)
    b_top, b_bot = b.quo_rem(x^m)
    R00, R01, R10, R11 = HGCD(a_top, b_top)
    c = R00 * a + R01 * b
    d = R10 * a + R11 * b
    q, e = c.quo_rem(d)
    d_top, d_bot = d.quo_rem(x^(m // 2))
    e_top, e_bot = e.quo_rem(x^(m // 2))
    S00, S01, S10, S11 = HGCD(d_top, e_top)
    RET00 = S01 * R00 + (S00 - q * S01) * R10
    RET01 = S01 * R01 + (S00 - q * S01) * R11
    RET10 = S11 * R00 + (S10 - q * S11) * R10
    RET11 = S11 * R01 + (S10 - q * S11) * R11
    return RET00, RET01, RET10, RET11

def GCD(a, b):
    print(a.degree(), b.degree())
    q, r = a.quo_rem(b)
    if r == 0:
        return b
    R00, R01, R10, R11 = HGCD(a, b)
    c = R00 * a + R01 * b
    d = R10 * a + R11 * b
    if d == 0:
        return c.monic()
    q, r = c.quo_rem(d)
    if r == 0:
        return d
    return GCD(d, r)

sys.setrecursionlimit(500000)

e = 65537
n1 = 
n2 = 
c1 = 
c2 = 
R.<x> = PolynomialRing(Zmod(n2))
f = x^e - c1
g = (n1 - x)^e - c2

res = GCD(f,g)

m = -res.monic().coefficients()[0]
print(m)
flag = long_to_bytes(int(m))
print(flag)
#CnHongKe{Fr4nkl1n_R31ter_4nd_gcD}

十八、Boneh-Durfee攻击

攻击条件:\(d < N^{0.292}\)

存脚本即可

exp.sage

Python
from __future__ import print_function
import time

############################################
## Config
##########################################

"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True

"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False

"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 ## stop removing if lattice reaches that dimension

############################################
## Functions
##########################################

## display stats on helpful vectors
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii,ii] >= modulus:
            nothelpful += 1

    print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

## display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii,jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print(a)

## tries to remove unhelpful vectors
## we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
    ## end of our recursive function
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    ## we start by checking from the end
    for ii in range(current, -1, -1):
        ## if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            ## let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                ## if another vector is affected:
                ## we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            ## level:0
            ## if no other vectors end up affected
            ## we remove it
            if affected_vectors == 0:
                print("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii-1)
                return BB

            ## level:1
            ## if just one was affected we check
            ## if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    ## if it is affecting even one vector
                    ## we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                ## remove both it if no other vector was affected and
                ## this helpful vector is not helpful enough
                ## compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
                    print("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii-1)
                    return BB
    ## nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

    finds a solution if:
    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
    whenever delta < 1 - sqrt(2)/2 ~ 0.292
    """

    ## substitution (Herrman and May)
    PR.<u, x, y> = PolynomialRing(ZZ)
    Q = PR.quotient(x*y + 1 - u) ## u = xy + 1
    polZ = Q(pol).lift()

    UU = XX*YY + 1

    ## x-shifts
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
            gg.append(xshift)
    gg.sort()

    ## x-shifts list of monomials
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial not in monomials:
                monomials.append(monomial)
    monomials.sort()

    ## y-shifts (selected by Herrman and May)
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift) ## substitution

    ## y-shifts list of monomials
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            monomials.append(u^kk * y^jj)

    ## construct lattice B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

    ## Prototype to reduce the lattice
    if helpful_only:
        ## automatically remove
        BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
        ## reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print("failure")
            return 0,0

    ## check if vectors are helpful
    if debug:
        helpful_vectors(BB, modulus^mm)

    ## check if determinant is correctly bounded
    det = BB.det()
    bound = modulus^(mm*nn)
    if det >= bound:
        print("We do not have det < bound. Solutions might not be found.")
        print("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    ## display the lattice basis
    if debug:
        matrix_overview(BB, modulus^mm)

    ## LLL
    if debug:
        print("optimizing basis of the lattice via LLL, this can take a long time")

    BB = BB.LLL()

    if debug:
        print("LLL is done!")

    ## transform vector i & j -> polynomials 1 & 2
    if debug:
        print("looking for independent vectors in the lattice")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            ## for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
                pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

            ## resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            ## are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    ## solutions
    soly = rr.roots()

    if len(soly) == 0:
        print("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly

def example():
    ############################################
    ## How To Use This Script
    ##########################################

    #
    ## The problem to solve (edit the following values)
    #

    ## the modulus
    N = 0xc2fd2913bae61f845ac94e4ee1bb10d8531dda830d31bb221dac5f179a8f883f15046d7aa179aff848db2734b8f88cc73d09f35c445c74ee35b01a96eb7b0a6ad9cb9ccd6c02c3f8c55ecabb55501bb2c318a38cac2db69d510e152756054aaed064ac2a454e46d9b3b755b67b46906fbff8dd9aeca6755909333f5f81bf74db
    ## the public exponent
    e = 0x19441f679c9609f2484eb9b2658d7138252b847b2ed8ad182be7976ed57a3e441af14897ce041f3e07916445b88181c22f510150584eee4b0f776a5a487a4472a99f2ddc95efdd2b380ab4480533808b8c92e63ace57fb42bac8315fa487d03bec86d854314bc2ec4f99b192bb98710be151599d60f224114f6b33f47e357517

    ## the hypothesis on the private exponent (the theoretical maximum is 0.292)
    delta = .18 ## this means that d < N^delta

    #
    ## Lattice (tweak those values)
    #

    ## you should tweak this (after a first run), (e.g. increment it until a solution is found)
    m = 4 ## size of the lattice (bigger the better/slower)

    ## you need to be a lattice master to tweak these
    t = int((1-2*delta) * m)  ## optimization from Herrmann and May
    X = 2*floor(N^delta)  ## this _might_ be too much
    Y = floor(N^(1/2))    ## correct if p, q are ~ same size

    #
    ## Don't touch anything below
    #

    ## Problem put in equation
    P.<x,y> = PolynomialRing(ZZ)
    A = int((N+1)/2)
    pol = 1 + x * (A + y)

    #
    ## Find the solutions!
    #

    ## Checking bounds
    if debug:
        print("=== checking values ===")
        print("* delta:", delta)
        print("* delta < 0.292", delta < 0.292)
        print("* size of e:", int(log(e)/log(2)))
        print("* size of N:", int(log(N)/log(2)))
        print("* m:", m, ", t:", t)

    ## boneh_durfee
    if debug:
        print("=== running algorithm ===")
        start_time = time.time()

    solx, soly = boneh_durfee(pol, e, m, t, X, Y)

    ## found a solution?
    if solx > 0:
        print("=== solution found ===")
        if False:
            print("x:", solx)
            print("y:", soly)

        d = int(pol(solx, soly) / e)
        print("private key found:", d)
    else:
        print("=== no solution was found ===")

    if debug:
        print(("=== %s seconds ===" % (time.time() - start_time)))

if __name__ == "__main__":
    example()

例题 2023楚慧杯——so large e

task.py

Python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from flag import flag
import random

m = bytes_to_long(flag)

p = getPrime(512)
q = getPrime(512)
n = p*q
e = random.getrandbits(1024)
assert size(e)==1024
phi = (p-1)*(q-1)
assert GCD(e,phi)==1
d = inverse(e,phi)
assert size(d)==269

pub = (n, e)
PublicKey = RSA.construct(pub)
with open('pub.pem', 'wb') as f :
    f.write(PublicKey.exportKey())

c = pow(m,e,n)
print('c =',c)

print(long_to_bytes(pow(c,d,n)))


#c = 6838759631922176040297411386959306230064807618456930982742841698524622016849807235726065272136043603027166249075560058232683230155346614429566511309977857815138004298815137913729662337535371277019856193898546849896085411001528569293727010020290576888205244471943227253000727727343731590226737192613447347860

提取公钥得到n,e

发现\(\frac{d}{n} = 0.26269\)

多半就是boneh_durfee攻击手段

改改example()函数的deltam即可

delta就是\(\frac{d}{n}\)的大小

exp

Python
#sage
from __future__ import print_function
import time

############################################
## Config
##########################################

"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True

"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False

"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 ## stop removing if lattice reaches that dimension

############################################
## Functions
##########################################

## display stats on helpful vectors
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii,ii] >= modulus:
            nothelpful += 1

    print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

## display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii,jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print(a)

## tries to remove unhelpful vectors
## we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
    ## end of our recursive function
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    ## we start by checking from the end
    for ii in range(current, -1, -1):
        ## if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            ## let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                ## if another vector is affected:
                ## we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            ## level:0
            ## if no other vectors end up affected
            ## we remove it
            if affected_vectors == 0:
                print("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii-1)
                return BB

            ## level:1
            ## if just one was affected we check
            ## if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    ## if it is affecting even one vector
                    ## we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                ## remove both it if no other vector was affected and
                ## this helpful vector is not helpful enough
                ## compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
                    print("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii-1)
                    return BB
    ## nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

    finds a solution if:
    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
    whenever delta < 1 - sqrt(2)/2 ~ 0.292
    """

    ## substitution (Herrman and May)
    PR.<u, x, y> = PolynomialRing(ZZ)
    Q = PR.quotient(x*y + 1 - u) ## u = xy + 1
    polZ = Q(pol).lift()

    UU = XX*YY + 1

    ## x-shifts
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
            gg.append(xshift)
    gg.sort()

    ## x-shifts list of monomials
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial not in monomials:
                monomials.append(monomial)
    monomials.sort()

    ## y-shifts (selected by Herrman and May)
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift) ## substitution

    ## y-shifts list of monomials
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            monomials.append(u^kk * y^jj)

    ## construct lattice B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

    ## Prototype to reduce the lattice
    if helpful_only:
        ## automatically remove
        BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
        ## reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print("failure")
            return 0,0

    ## check if vectors are helpful
    if debug:
        helpful_vectors(BB, modulus^mm)

    ## check if determinant is correctly bounded
    det = BB.det()
    bound = modulus^(mm*nn)
    if det >= bound:
        print("We do not have det < bound. Solutions might not be found.")
        print("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    ## display the lattice basis
    if debug:
        matrix_overview(BB, modulus^mm)

    ## LLL
    if debug:
        print("optimizing basis of the lattice via LLL, this can take a long time")

    BB = BB.LLL()

    if debug:
        print("LLL is done!")

    ## transform vector i & j -> polynomials 1 & 2
    if debug:
        print("looking for independent vectors in the lattice")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            ## for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
                pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

            ## resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            ## are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    ## solutions
    soly = rr.roots()

    if len(soly) == 0:
        print("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly

def example():
    ############################################
    ## How To Use This Script
    ##########################################

    #
    ## The problem to solve (edit the following values)
    #

    ## the modulus
    N = 116518679305515263290840706715579691213922169271634579327519562902613543582623449606741546472920401997930041388553141909069487589461948798111698856100819163407893673249162209631978914843896272256274862501461321020961958367098759183487116417487922645782638510876609728886007680825340200888068103951956139343723
    e = 113449247876071397911206070019495939088171696712182747502133063172021565345788627261740950665891922659340020397229619329204520999096535909867327960323598168596664323692312516466648588320607291284630435682282630745947689431909998401389566081966753438869725583665294310689820290368901166811028660086977458571233
    ## the public exponent
    ## the hypothesis on the private exponent (the theoretical maximum is 0.292)
    delta = .27 ## this means that d < N^delta

    #
    ## Lattice (tweak those values)
    #

    ## you should tweak this (after a first run), (e.g. increment it until a solution is found)
    m = 5 ## size of the lattice (bigger the better/slower)

    ## you need to be a lattice master to tweak these
    t = int((1-2*delta) * m)  ## optimization from Herrmann and May
    X = 2*floor(N^delta)  ## this _might_ be too much
    Y = floor(N^(1/2))    ## correct if p, q are ~ same size

    #
    ## Don't touch anything below
    #

    ## Problem put in equation
    P.<x,y> = PolynomialRing(ZZ)
    A = int((N+1)/2)
    pol = 1 + x * (A + y)

    #
    ## Find the solutions!
    #

    ## Checking bounds
    if debug:
        print("=== checking values ===")
        print("* delta:", delta)
        print("* delta < 0.292", delta < 0.292)
        print("* size of e:", int(log(e)/log(2)))
        print("* size of N:", int(log(N)/log(2)))
        print("* m:", m, ", t:", t)

    ## boneh_durfee
    if debug:
        print("=== running algorithm ===")
        start_time = time.time()

    solx, soly = boneh_durfee(pol, e, m, t, X, Y)

    ## found a solution?
    if solx > 0:
        print("=== solution found ===")
        if False:
            print("x:", solx)
            print("y:", soly)

        d = int(pol(solx, soly) / e)
        print("private key found:", d)
    else:
        print("=== no solution was found ===")

    if debug:
        print(("=== %s seconds ===" % (time.time() - start_time)))

if __name__ == "__main__":
    example()

例题 2023领航杯 bd

task.py

Python
from Crypto.Util.number import *
from secret import flag

p = getPrime(512)
q = getPrime(512)
n = p * q
d = getPrime(299)
e = inverse(d,(p-1)*(q-1))
m = bytes_to_long(flag)
c = pow(m,e,n)
hint1 = p >> (512-70)
hint2 = q >> (512-70)


print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")
print(f"hint1 = {hint1}")
print(f"hint2 = {hint2}")

"""
n = 73337798113265277242402875272164983073482378701520700321577706460042584510776095519204866950129951930143711572581533177043149866218358557626070702546982947219557280493881836314492046745063916644418320245218549690820002504737756133747743286301499039227014032044403571945455215839074583290324966069724343874361
e = 42681919079074901709680276679968298324860328305878264036188155781983964226653746568102282190906458519960811259171162918944726137301701132135900454469634110653076655027353831375989861927565774719655974876907429954299669710134188543166679161864800926130527741511760447090995444554722545165685959110788876766283
c = 35616516401097721876690503261383371143934066789806504179229622323608172352486702183654617788750099596415052166506074598646146147151595929618406796332682042252530491640781065608144381326123387506000855818316664510273026302748073274714692374375426255513608075674924804166600192903250052744024508330641045908599
hint1 = 740477612377832718425
hint2 = 767891335159501447918
"""

这是一种利用上p,q高位的boneh-durfee,论文:367.pdf (iacr.org)

对大佬实验的脚本进行修改:Codes of the manuscript---Practical Attacks on Small Private Exponent RSA---New Records and New Insi - Pastebin.com

这类题没太搞懂原理,只能当个脚本小子先

exp

Python
import time
time.clock = time.time

debug = True

strict = False

helpful_only = True
dimension_min = 7 ## 如果晶格达到该尺寸,则停止移除
## 显示有用矢量的统计数据
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii,ii] >= modulus:
            nothelpful += 1

    print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

## 显示带有 0 和 X 的矩阵
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii,jj] == 0 else 'X'
            if BB.dimensions()[0] < 60: 
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        #print (a)

## 尝试删除无用的向量
## 从当前 = n-1(最后一个向量)开始
def remove_unhelpful(BB, monomials, bound, current):
    ## 我们从当前 = n-1(最后一个向量)开始
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    ## 开始从后面检查
    for ii in range(current, -1, -1):
        ##  如果它没有用
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
             ## 让我们检查它是否影响其他向量
            for jj in range(ii + 1, BB.dimensions()[0]):
                ## 如果另一个向量受到影响:
                ## 我们增加计数
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            ## 等级:0
            ## 如果没有其他载体最终受到影响
            ## 我们删除它
            if affected_vectors == 0:
                #print ("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii-1)
                return BB

           ## 等级:1
            #如果只有一个受到影响,我们会检查
            ## 如果它正在影响别的向量
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    ## 如果它影响哪怕一个向量
                    ## 我们放弃这个
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                ## 如果没有其他向量受到影响,则将其删除,并且
                ## 这个有用的向量不够有用
                #与我们无用的相比
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
                    #print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii-1)
                    return BB
    ## nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 如果 "strict=true",并且行列式不受约束
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

 在以下情况下找到解决方案:
* d < N^delta
* |x|< e^delta
* |y|< e^0.5
每当 delta < 1 - sqrt(2)/2 ~ 0.292
    """

    ## substitution (Herrman and May)
    PR.<u, x, y> = PolynomialRing(ZZ)   #多项式环
    Q = PR.quotient(x*y + 1 - u)        ##  u = xy + 1
    polZ = Q(pol).lift()

    UU = XX*YY + 1

    ## x-移位
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
            gg.append(xshift)
    gg.sort()

    ## 单项式 x 移位列表
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials(): #对于多项式中的单项式。单项式():
            if monomial not in monomials:  ## 如果单项不在单项中
                monomials.append(monomial)
    monomials.sort()

    ## y-移位
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift) ## substitution

    ## 单项式 y 移位列表
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            monomials.append(u^kk * y^jj)

    ## 构造格 B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

    #约化格的原型
    if helpful_only:
        ##  #自动删除
        BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
        ## 重置维度
        nn = BB.dimensions()[0]
        if nn == 0:
            print ("failure")
            return 0,0

    ## 检查向量是否有帮助
    if debug:
        helpful_vectors(BB, modulus^mm)

    ## 检查行列式是否正确界定
    det = BB.det()
    bound = modulus^(mm*nn)
    if det >= bound:
        print ("We do not have det < bound. Solutions might not be found.")
        print ("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print ("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    ## display the lattice basis
    if debug:
        matrix_overview(BB, modulus^mm)

    ## LLL
    if debug:
        print ("optimizing basis of the lattice via LLL, this can take a long time")

    #BB = BB.BKZ(block_size=25)
    BB = BB.LLL()

    if debug:
        print ("LLL is done!")

    ## 替换向量 i 和 j ->多项式 1 和 2
    if debug:
        print ("在格中寻找线性无关向量")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):

            ## 对于i and j, 构造两个多项式

            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
                pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

            ## 结果
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)


            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print ("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print ("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    ## solutions
    soly = rr.roots()

    if len(soly) == 0:
        print ("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]
    return solx, soly

def example():
    ############################################
    ## 随机生成数据
    ##########################################
    #start_time =time.perf_counter
    start =time.clock()
    size = 512
    length_N = 2*size
    ss = 0
    s = 70
    M = 1   ## the number of experiments
    delta = 299/1024

    for i in range(M):
##         p =  random_prime(2^size,None,2^(size-1))
##         q =  random_prime(2^size,None,2^(size-1))
##         if(p<q):
##             temp=p
##             p=q
##             q=temp
        N = 73337798113265277242402875272164983073482378701520700321577706460042584510776095519204866950129951930143711572581533177043149866218358557626070702546982947219557280493881836314492046745063916644418320245218549690820002504737756133747743286301499039227014032044403571945455215839074583290324966069724343874361
        e = 42681919079074901709680276679968298324860328305878264036188155781983964226653746568102282190906458519960811259171162918944726137301701132135900454469634110653076655027353831375989861927565774719655974876907429954299669710134188543166679161864800926130527741511760447090995444554722545165685959110788876766283
        hint1 = 740477612377832718425   ## p高位
        hint2 = 767891335159501447918  ## q高位
##         print ("p真实高",s,"比特:", int(p/2^(512-s)))
##         print ("q真实高",s,"比特:", int(q/2^(512-s)))
##         N = p*q;


    ## 解密指数d的指数( 最大0.292)  

        m = 7   ## 格大小(越大越好/越慢)
        t = round(((1-2*delta) * m))  ## 来自 Herrmann 和 May 的优化
        X = floor(N^delta)  ## 
        Y = floor(N^(1/2)/2^s)    ## 如果 p、 q 大小相同,则正确
        for l in range(int(hint1),int(hint1)+1):
            print('\n\n\n l=',l)
            pM=l;
            p0=pM*2^(size-s)+2^(size-s)-1;
            q0=N/p0;
            qM=int(q0/2^(size-s))
            A = N + 1-pM*2^(size-s)-qM*2^(size-s);
        #A = N+1
            P.<x,y> = PolynomialRing(ZZ)
            pol = 1 + x * (A + y)  #构建的方程

            ## Checking bounds
            #if debug:
                #print ("=== 核对数据 ===")
                #print ("* delta:", delta)
                #print ("* delta < 0.292", delta < 0.292)
                #print ("* size of e:", ceil(log(e)/log(2)))  ## e的bit数
                ## print ("* size of N:", len(bin(N)))          ## N的bit数
                #print ("* size of N:", ceil(log(N)/log(2)))  ## N的bit数
                #print ("* m:", m, ", t:", t)

            ## boneh_durfee
            if debug:
                ##print ("=== running algorithm ===")
                start_time = time.time()


            solx, soly = boneh_durfee(pol, e, m, t, X, Y)


            if solx > 0:
                #print ("=== solution found ===")
                if False:
                    print ("x:", solx)
                    print ("y:", soly)

                d_sol = int(pol(solx, soly) / e)
                ss=ss+1

                print ("=== solution found ===")
                print ("p的高比特为:",l)
                print ("q的高比特为:",qM)
                print ("d=",d_sol) 

            if debug:
                print("=== %s seconds ===" % (time.time() - start_time))
            #break
        print("ss=",ss)
                            #end=time.process_time
        end=time.clock()
        print('Running time: %s Seconds'%(end-start))
if __name__ == "__main__":
    example()  

十九、一些和数论推导相关的题目

1.hint = pow(ap+b,q,n)

task.py

Python
import libnum
import gmpy2
import uuid
from Crypto.Util.number import *

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)

e = 65537
p = getPrime(1024)
q = getPrime(1024
n = p * q
c = pow(m, e, n)
hint = pow(2020 * p + 2021, q, n)
print(f'n={n}')
print(f'c={c}')
print(f'hint={hint}')

把hint展开

\[ hint \equiv (2020p + 2021)^q \mod n \]

\(p\)能得到

\[ hint \equiv 2021^q \mod p \]

两边同时取p次方

\[ hint^p \equiv 2021^n \mod p \]

\[ hint \equiv 2021^n \mod p \]

p = gcd(pow(2021,n,n) - hint,n)

exp

Python
from Crypto.Util.number import *
import gmpy2

n = 27020725261160598541077357737650775795182555998856810737571508044949928734067444441660366270392732456051807439301564552672200975582350577967001486740668193835704559129378410828266554536148151615878808327988333060924165410762016082268322936465413880236634083213834739549234631742766416876749808978934944262651307600621868854944164060642189749365967978497831698002669974744487926082412272998646851047638183126945784060957075393737537570645086672473571281053798891685646561828588448040073373363454584468753860529849749093081434144360661566815886630699933232263079413562069476421802192735693386029862725050469209845710383
c = 10188807385387617708190575473905502994151677148079820873886980571555051900701810208218351138721306416600688313703084580808183634201231599134123549448962443376514560489130860694363901933597676373555599555647232128717993571185822894818704143675318690577221330618533739592165564396729937983659337232822442555504262694675199751730664450120569727835850996566436129543730932040989365233424791093843941154003052950306359994891955336607690065213304872738280674213630736611351982705373394299097653653497017756036211550125607093109216729483090391729134062236908282557149575812220142872855836932590459512028618076264332235518829
hint = 15179972145975733285419381814235528011288991423484121653543845156913121513320504879647666067298415751234264897435338898933073713420024176276221164394369781676781604128149168834126855517212300158864797800121336042194751965268493010327202598446572764475343894613152062609436699715193914479572113800212525965140106015838636914979735618606768207651697548364440806425770871133439416876157686985836939255598747973339866125864303982956813846287509191028829738926404992619459242904729015823730553526572575372668559669124599614613391797015393641171177282129497503752370800088634017972208535899870704612274473042064675033593148
e = 65537

p = gmpy2.gcd(pow(2021,n,n)-hint,n)
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

2.hint = pow(ap+b,e,n)

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
print(flag)
m = libnum.s2n(flag)
p = libnum.generate_prime(512)
q = libnum.generate_prime(512)
e = 65537
n = p * q
h = 20211102
hc = pow(h + p * 1111, e, n)
c = pow(m, e, n)
print("hc=", hc)
print("n=", n)
print("c=", c)
hc = 71505320953946158049530109094654497075489963071106175336722892393493112481336409391299522595724154571954223093317880494307263262649833222750675105885636892419350501821324979706283867758665536771783209816719106279467902518895579024290387800216711663670572861182058425925280993190282267615052256942516011995207
n = 76856511192427852645963041043072791148703422665129663050712492700760489247788743818199589072069758934570218804936479267319288093436111548055922916898782764333246946326823653877357695179165138863843657328764265204547147092074499832221138997131011222722917338444675582832114206750168113207646100633238664244737
c = 39246179387125192271554620313966311736032348078183121707012959204367908070472764506984235827179206718838172586811066682034907967943722841257765922283692526422653916506577810629430853963448057701574209912936660396486847365579797147723437378122880075493171892191049105237005801787649587080840600670585409293046
\[ hint \equiv (ap + b)^e \mod n \]

两边模p得到

\[ hint \equiv b^e \mod p \]

p = gcd(pow(b,e,n)-hint,n)

exp

Python
from Crypto.Util.number import *
import gmpy2

hint = 71505320953946158049530109094654497075489963071106175336722892393493112481336409391299522595724154571954223093317880494307263262649833222750675105885636892419350501821324979706283867758665536771783209816719106279467902518895579024290387800216711663670572861182058425925280993190282267615052256942516011995207
n = 76856511192427852645963041043072791148703422665129663050712492700760489247788743818199589072069758934570218804936479267319288093436111548055922916898782764333246946326823653877357695179165138863843657328764265204547147092074499832221138997131011222722917338444675582832114206750168113207646100633238664244737
c = 39246179387125192271554620313966311736032348078183121707012959204367908070472764506984235827179206718838172586811066682034907967943722841257765922283692526422653916506577810629430853963448057701574209912936660396486847365579797147723437378122880075493171892191049105237005801787649587080840600670585409293046
e = 65537
b = 20211102

p = gmpy2.gcd(pow(b,e,n)-hint,n)
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

3.q = inverse(e,p)

task.py

Python
import gmpy2
import libnum
import uuid

flag = "flag{" + str(uuid.uuid4()) + "}"
m = libnum.s2n(flag)
e = 65537

while 1:
    p = libnum.generate_prime(512)
    q = libnum.invmod(e, p)
    if gmpy2.is_prime(q):
        break
n=p*q
c=pow(m,e,n)
print("n=",n)
print("c=",c)
\[ eq \equiv 1 \mod p \]

所以有

\[ eq = kp + 1 \]

两边同时乘\(p\)

\[ en = kp^2 + 1 \]

这个k和e差不多大,在e不太大的情况下可以枚举k解这个方程

还有个方法就是

\(en = kp^2 + 1\),凑出\(4ken + 1 = (2kp+1)^2\)

所以\(p = \frac{(4ken+1)^{\frac{1}{2}}-1}{2k}\)

exp.py

Python
from Crypto.Util.number import *
import gmpy2

n = 58204114420266684815970658378327773998564393394613074044159240444415512492622689982761518905542522328879289101538953676661805875053162972893258897360344016406294652339343767887745752686437325346837603712186500309908703326587069304255508650910107794737000566778637055164127273830551001009133332601839918695739
c = 43430611858598126654595145883807180034546121754009334579568652220639483233618841529702503298293207886941913478597477904682701187000781983315547295293641024286170190626012102497319037012751300959255076727985925313853559910457404758461819072120343343943528618993507783981659418177044469142912677141806591464788
e = 65537

for k in range(1,e):
    t = gmpy2.iroot((4*k*e*n + 1),2)
    if t[1]:
        p = (t[0]-1) // (2*k)
        q = n // p
        d = gmpy2.invert(e,(p-1)*(q-1))
        m = pow(c,d,n)
        print(long_to_bytes(m))
        break

4.pow(m,p,n)和pow(m,q,n)

task.py

Python
import libnum
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m=libnum.s2n(flag)
p=libnum.generate_prime(512)
q=libnum.generate_prime(512)
n=p*q
x=pow(m,p,n)
y=pow(m,q,n)
print("n=",n)
print("x=",x)
print("y=",y)
\[ x \equiv m ^ p \mod n \]

由费马小定理得

\[ x \equiv m \mod p \]

同理

\[ y \equiv m \mod q \]

\[ x = k_1p + m \]
\[ y = k_2q + m \]

相乘之后得到

\[ xy = m^2 + (k_1p+k_2q)m + k_1k_2n \]

再次化简

\[ xy \equiv m^2 + (x-m + y - m)m \mod n \]

然后用copper解

exp.sage

Python
from Crypto.Util.number import *

n = 119168018353396473071220326930634033296857570351877525203349067654228903288865563346158289110924833018314600851680403536994000145188490252887831026813814036968773088160020677568623347649228357482873911613625537196000702331852604517461327776409524836030721808860499640682583008287503643735346752806150919473547
x = 20889450245206188360163017466832661797042512279294313816641964974342193835127401896327018708629718120262511077145232274218469726065543657294173612852238031375768722870519066517281989421725687761586428233882899166319861093186744865410310768961853709659722549327714475931676224727563796969392665870925936060875
y = 32080707538492572190029237251694217148237404571842762312567934187316746458298295007401945958852986479911593108750816992042333214302313415615710205763277908672519589955718578006218124903939275787171888825356704546781455848117466648569999021535956085557973651213126679157398458139672913801297571585910183741493

R.<m> = PolynomialRing(Zmod(n))

f = x*y - m^2 - m*(x - m + y - m)
f = f.monic()
root = f.small_roots(X=2^336)
if root != []:
    print(long_to_bytes(int(root[0])))

5.leak = d + p

task.py

Python
from Crypto.Util.number import *
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"

m = bytes_to_long(flag.encode())
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
d = inverse(e,(p-1)*(q-1))

leak = d + p
c = pow(m,e,n)
print(f"leak = {leak}")
print(f"c = {c}")
print(f"n = {n}")
\[ leak = d + p \]

同时乘上e

\[ leak \times e = ed + ep \]
\[ \because ed = k(p-1)(q-1) + 1 \]
\[ \therefore leak \times e = k(p-1)(q-1) + ep + 1 = (k_1 + e)(p-1) + e + 1 \]

\[ 2^{leak\times e} \equiv 2^{(p-1)(k_1+e)+e+1} \equiv 2^{e+1} \mod p \]

p = gcd(pow(2,e*leak,n) - pow(2,e+1,n),n)

exp.py

Python
from Crypto.Util.number import *
import gmpy2

leak = 43773730595208010615167769243389712919043986781372553925103531305753192121900940758872887224717935746491356121136709395293427821519713664465479239343568683512805566797519940659245386786069452631897152582632812964236464253538638932777814445952543179981728372597574240011522586022744496408528369815424689627714
c = 32609109759602838902299290553935199233727818772965249106067660946940666082748851189532358246129713630287421754985189526470019256048805899987261287835158990237267846168718587057488187109882012577904592602367187436017302897260666096753156638501800052563311351183911829268477864436682312113353547663292164618797
n = 73687428902140845363357908478989818544523419338611246958530518113252515978963884581173646615800353308789787478447973996695401703969420384980841285031836556985268236880854319562700104921035182736733530699304849659644263120110205601793351720111745179867010541532557489688430577820860370652773129870929371931513
e = 65537

p = gmpy2.gcd(pow(2,leak * e,n) - pow(2,e + 1,n),n)
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

接下来三个题型来源于2023 数据安全产业人才能力挑战赛---math_exam

6.leak = (n + p) % (q-1)

task.py

Python
import libnum
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m=libnum.s2n(flag)
p=libnum.generate_prime(512)
q=libnum.generate_prime(512)
if p <= q:
    p, q = q, p
e = 0x10001
n = p * q
c = pow(m, e, n)
leak = (n + p) % (q-1)
print(f'{e = }')
print(f'{c = }')
print(f'{n = }')
print(f'{leak = }')

#e = 65537
#c = 846622880180923496523897101093587388412673197929101816777894080453907929979417582433675846206418833989406082027835614960931951892393700870678469052366477889491688459558189367255658278968722099012534045477993924284003153851171132420751750724394405070269223797513951991873714156884423926582172189394011114952
#n = 159452716285492879108396025652299064897348745847754101475050126796260046052685162344131542880739092630022413124977374334652652450809186959288635651980201026825896903174847709657312399801033678426271413442323084354665643010763178857967804623680894444913369256168333223214048037661178587978535722147091129404833
#leak = 2669103705669857828725305123419482758456336465207378766363532584323475128597302561377245054918781031448710846825859151788151496659511563529987056456136018
\[ leak \equiv (n + p) \mod q-1 \]

\[ leak \equiv p(q + 1) \mod q-1 \]
\[ \therefore leak \equiv 2p \mod q-1 \]

题目确定了\(p > q\)

\[ \therefore p \mod q-1 = p - q + 1 \]
\[ \therefore leak \equiv 2(p-q + 1) \]

再联立\(n = p \times q\)解方程即可分解n

exp.py

Python
from Crypto.Util.number import *
from sympy import solve,symbols
import gmpy2

e = 65537
c = 846622880180923496523897101093587388412673197929101816777894080453907929979417582433675846206418833989406082027835614960931951892393700870678469052366477889491688459558189367255658278968722099012534045477993924284003153851171132420751750724394405070269223797513951991873714156884423926582172189394011114952
n = 159452716285492879108396025652299064897348745847754101475050126796260046052685162344131542880739092630022413124977374334652652450809186959288635651980201026825896903174847709657312399801033678426271413442323084354665643010763178857967804623680894444913369256168333223214048037661178587978535722147091129404833
leak = 2669103705669857828725305123419482758456336465207378766363532584323475128597302561377245054918781031448710846825859151788151496659511563529987056456136018

p = symbols('p')
q = symbols('q')

eq1 = 2*(p-q+1) - leak
eq2 = p*q - n

solution = solve((eq1,eq2),(p,q))
print(solution)

p = int(solution[1][0])
q = int(solution[1][1])
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

7.leak = d + p + q

task.py

Python
import libnum
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m=libnum.s2n(flag)
p=libnum.generate_prime(512)
q=libnum.generate_prime(512)

e = 0x10001
n = p * q
c = pow(m, e, n)
d=libnum.invmod(e,(p-1)*(q-1))
leak = d+p+q
print(f'{e = }')
print(f'{c = }')
print(f'{n = }')
print(f'{leak = }')

#e = 65537
#c = 65194823373091792824101730983124740337276083358867621564067880339279446092377567969207237986613196892737474455781661885696122256698478429450971196633786693648866129636737262041654769618993094832127875251068775632567036646408374963058703320605733921797230106462440860984746880466416824674570639694481298708147
#n = 101929257180069933443442160085713121307068880188090652033345240568607589666231813199686824369361640378636124324844158355533056421197926269617986872835671463371353540696806173813019752742218460997272803573114119876536384486655117245296360474103378827486280279523273344156760793016731778663058274770626116861407
#leak = 87007773233816810147344015772388866522443114032719766646343787373996588014373656496789294836431302130125588646180886383013958151442324020038131736683796464202667423644714259545194662934238144990354387370533656134036753530002005551694211595695927699196016268145816389892292091218491443784193805171731592693841
\[ leak = d + p + q \]
\[ \therefore d = leak - (p + q) \]

\[ m \equiv c^d \equiv c^{leak -(p+q)} \mod n \]

因为\(\phi(n) = n - (p+q) + 1\)

\[ \therefore m \equiv c^{leak - (n + 1)} \mod n \]

exp.py

Python
from Crypto.Util.number import *

c = 65194823373091792824101730983124740337276083358867621564067880339279446092377567969207237986613196892737474455781661885696122256698478429450971196633786693648866129636737262041654769618993094832127875251068775632567036646408374963058703320605733921797230106462440860984746880466416824674570639694481298708147
n = 101929257180069933443442160085713121307068880188090652033345240568607589666231813199686824369361640378636124324844158355533056421197926269617986872835671463371353540696806173813019752742218460997272803573114119876536384486655117245296360474103378827486280279523273344156760793016731778663058274770626116861407
leak = 87007773233816810147344015772388866522443114032719766646343787373996588014373656496789294836431302130125588646180886383013958151442324020038131736683796464202667423644714259545194662934238144990354387370533656134036753530002005551694211595695927699196016268145816389892292091218491443784193805171731592693841

m = pow(c,leak - n - 1,n)
print(long_to_bytes(m))

8.leak = pow(p,q,n) + pow(q,p,n) % n

task.py

Python
import libnum
import uuid
flag = "flag{" + str(uuid.uuid4()) + "}"
m=libnum.s2n(flag)
p=libnum.generate_prime(512)
q=libnum.generate_prime(512)

e = 0x10001
n = p * q
c = pow(m, e, n)
leak = (pow(p, q, n) + pow(q, p, n)) % n
print(f'{e = }')
print(f'{c = }')
print(f'{n = }')
print(f'{leak = }')
\[ leak \equiv (p^q \mod n + q^p \mod n) \mod n \]

由费马小定理得

\[ leak \equiv (p^q \mod q + q^p \mod p) \mod n \]

\[ leak \equiv p + q \mod n \]
\[ \because p + q < n \]
\[ \therefore leak = p + q \]

然后

\[ (p-q)^2 = (p+q)^2 - 4n = leak^2 - 4n \]
\[ \therefore p = \frac{((p+q) + (p-q))}{2} \]

或者解方程

exp.py

Python
from Crypto.Util.number import *
import gmpy2

e = 65537
c = 59662555342583061013008608133060203475725526601468647442902301335538953096485921516133656618941085620436784211565880744663573927593670579237831797055934897166262528476227281479029026508166848256301828084036716500159067642101104810756620735383857351274773983199968924981397675373272878756685629789497697821620
n = 83332115751539889489689110273690067288993797655970253065863170986174973047785854940017477990345318506407680986257706329521142524295434171889087917406552261883625775754882538291980506944585738241124811588555071095223782766762626040473256423491630224616140407276984106458673447870374272906086783555489477673207
leak = 18881487897809480964326513919135880296801378921812225600834164247018332292886076618571738627353925482046410714540439662613766662197119044934743578662330528

p_q = gmpy2.iroot(leak ** 2 - 4 * n,2)[0]
p = (leak + p_q) // 2
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

2023数据安全产业人才能力挑战赛——math_exam

task.py

Python
import os
from Crypto.Util.number import *
from secret import flag

bits = 512

def pad(msg, length):
    pad_length = length - len(msg) - 1
    pad_data = os.urandom(pad_length)
    return msg + b'\x00' + pad_data

def unpad(msg):
    return msg.split(b"\x00")[0]

def challenge1(m):
    p, q = [getPrime(bits) for i in range(2)]
    if p <= q:
        p, q = q, p
    e = 0x10001
    n = p * q
    c = pow(m, e, n)
    leak = (n + p) % (q-1)
    print('-------- challenge 1 --------')
    print(f'{e = }')
    print(f'{c = }')
    print(f'{n = }')
    print(f'{leak = }')

def challenge2(m):
    p, q = [getPrime(bits) for i in range(2)]
    e = 0x10001
    n = p * q
    d = inverse(e, (p-1)*(q-1))
    c = pow(m, e, n)
    leak = d + p + q
    print('-------- challenge 2 --------')
    print(f'{e = }')
    print(f'{c = }')
    print(f'{n = }')
    print(f'{leak = }')

def challenge3(m):
    p, q = [getPrime(bits) for i in range(2)]
    e = 0x10001
    n = p * q
    c = pow(m, e, n)
    leak = (pow(p, q, n) + pow(q, p, n)) % n
    print('-------- challenge 3 --------')
    print(f'{e = }')
    print(f'{c = }')
    print(f'{n = }')
    print(f'{leak = }')

assert len(flag) == 42
ms = []
for i in range(0, 42, 14):
    ms.append(bytes_to_long(pad(flag[i:i+14], bits//4-1)))

m1, m2, m3 = ms
challenge1(m1)
challenge2(m2)
challenge3(m3)

"""
-------- challenge 1 --------
e = 65537
c = 112742443814287255411540092433156061065388404049949520959549377871297566383025041892192679147481155020865118811016470498351633875090973546567374001852295013083192495299811476604405637385307524194793969533646755764136014187430115618114840780368311166911900457224593131166970676797547489278410997707815915932756
n = 121127425328043404860413278637978444801342472819958112597540188142689720655042880213001676368390521140660355813910726809125567752172921410143437643574528335234973793653775043030021036875866776532570781661875022102733555943967261003246543180935987772711036868216508554536086688819118597075508026787867088355603
leak = 216638862637129382765636503118049146067015523924032194492700294200289728064297722088882791754351329407138196573832392846467607399504585045028165699421278
-------- challenge 2 --------
e = 65537
c = 7964477910021153997178145480752641882728907630831216554750778499596527781702830885213467912351097301767341858663701574005489585561370961723264247818377063081744522471774208105250855114831033452448184392499682147532404562876275189577321587660597603848038824026981539659156304028998137796242331160312370913038
n = 140571013522095816880929287025269553867630639381779595547026503691829940612178900269986625350464874598461222087427155791855120339533208468121389480964471710028253589422629569889402475311387750348466199387760629889238062977271925350490110043385800605640905324122017637306715108727700910035925728362455954862209
leak = 58442382248753295429370894053397615609981110383986887405127350139482893508400422595729520437678203735054593866306478994471465948872565590901376309380029015549809468112086393107585011072503638322671608471684607214064187044372418770555236721845694224676090744181562673509234801011420696349507624867568099759003
-------- challenge 3 --------
e = 65537
c = 54161995127842474543974770981473422085334044100057089719350274921419091368361244533281599379235907845996678762379778310924192757650322930707785543132446159092950451255660204858292974657119337026589911330412367633761103944916751660957776230135927005700707688661350641600954072696774954805514477330339449799540
n = 88207747624007183083381863279444163105330473097729276113333026679597864128605555600000789783468271680476780366740448641311570797876037993255307716167149079618302706650018518487351604778857406170722209469765782625409279109832638886179654096975665134276856272488090272822541461702907181545730309689190333058151
leak = 19596671928335648228117128090384865424885102632642665068992144783391306491716530155291726644158221224616817878768426330717188403310818678195631582453246848
"""

exp.py

Python
from sympy import symbols,solve
from Crypto.Util.number import *
import gmpy2

e = 65537
c1 = 
n1 = 
leak1 = 

p1 = symbols('p1')
q1 = symbols('q1')

eq1 = 2*(p1-q1+1)-leak1
eq2 = p1 * q1- n1

solution1 = solve((eq1,eq2),(p1,q1))

p1,q1 = int(solution1[1][0]),int(solution1[1][1])
d1 = gmpy2.invert(e,(p1-1)*(q1-1))
m1 = pow(c1,d1,n1)
flag1 = long_to_bytes(m1)[:14]

c2 = 
n2 = 
leak2 = 

tmp = leak2 - n2 -1
m2 = pow(c2,tmp,n2)
flag2 = long_to_bytes(m2)[:14]

c3 = 
n3 = 
leak3 = 

p3 = symbols('p3')
q3 = symbols('q3')

eq3 = p3 + q3 - leak3
eq4 = p3 * q3 - n3

solution3 = solve((eq3,eq4),(p3,q3))

p3,q3 = int(solution3[0][0]),int(solution3[0][1])
d3 = gmpy2.invert(e,(p3-1)*(q3-1))
m3 = pow(c3,d3,n3)
flag3 = long_to_bytes(m3)[:14]

flag = flag1 + flag2 + flag3
print(flag)

二十、剪枝相关

1.gift = p ^ q

题目来源:2023陕西省技能大赛——奇怪得asr

task.py

Python
from Crypto.Util.number import *

key = 'flag{**********}'

bits = 1024
msg = bytes_to_long(key.encode())

e = 65537
p = getPrime(bits)
q = getPrime(bits)
n = p * q

def encrypt1(msg, e, n):
    c = pow(msg, e, n)
    return c

seed = p ^ q
a = getPrime(bits)
b = getPrime(bits)
n1 = getPrime(bits)

def encrypt2(seed):
    enc = []
    for i in range(10):
        seed = (a * seed + b) % n1
        enc.append(seed)
    return enc

c = encrypt1(msg, e, n)
enc = encrypt2(seed)
print("n = ", n)
print("c = ", c)
print("n1 = ", n1)
print("enc = ", enc)

n =  24044063028844014127418595700558729326190738802687551098858513077613750188240082663594575453404975706225242363463089392757425008423696150244560748490108425645064339883915929498539109384801415313004805586193044292137299902797522618277016789979196782551492020031695781792205215671106103568559626617762521687128199445018651010056934305055040748892733145467040663073395258760159451903432330506383025685265502086582538667772105057401245864822281535425692919273252955571196166824113519446568745718898654447958192533288063735350717599092500158028352667339959012630051251024677881674246253876293205648190626145653304572328397
c =  14883053247652228283811442762780942186987432684268901119544211089991663825267989728286381980568977804079766160707988623895155236079459150322336701772385709429870215701045797411519212730389048862111088898917402253368572002593328131895422933030329446097639972123501482601377059155708292321789694103528266681104521268192526745361895856566384239849048923482217529011549596939269967690907738755747213669693953769070736092857407573675987242774763239531688324956444305397953424851627349331117467417542814921554060612622936755420459029769026126293588814831034143264949347763031994934813475762839410192390466491651507733968227
n1 =  137670797028117726329534659376416493367957852768263083700434198723955223922183386928456013703791817601151754417828367188186912209697081337658512940425529211281290630976671911327606706953154608427885071841566358882014021242768190762103365969320014710368160869517966437591299370072284930202718943785099916898209
enc =  [101737402423360536260958229788866250367716256968287178187558336481872788309727545478736771692477306412259739856568227009850831432381180909815512654609798228982433082928392936844193974517574281026029228179913579225687286945054175762659252515268270399329404664775893089132101252158524000295899895962104782878103, 37355684997487259669354747104430314505839306993101096210478266975184357608742619438151118843905165289324251734149329596611854110739738607745107961453008343886403511257039401245484528985856920723694142989180291902939107642020398816995584650913417698279936585230648639613028793148102494100898288564799111024672, 58677759595639211550435023449462812079890625834313820227189340593596480924226619376872336960357021314847975570175387751632125898437020801920862764666175594874885587518469384576361008639967382152477408865298759987606155830674598034578657554841283906976808719095766296677147076808250022898199866472085742989883, 61841632061818470036288407041172200048676249787061823756736224887116113640875444187463656719652972233582538657844183320242896612625995507633237074900538692102956750184024574603018257213912795847625926653585010890014291951218199774765624860625726555381815237888483974246173727262881650634287497285246796321130, 7618244158597756867387754433401378508070531356170836765779245254233413235386172690733378371343899289510629513166609513857423499004879497768588665836034791151090648182168421570449377835494883902907064269417199065924565304966242954268460876762295575715334403142360198583318323418975108290758222653083011275844, 106276841058222138994123556391380518368163552919305398852484130331884811278068151915582752795463570013359693610495645946230044828403849434903415989487924763756589202218361370725532394478569304449884620166937809374355282324069422109879874964479199929174533104879048175102339134830614476339153367475243140156049, 54574757236475194407137831004617398270525645136836468973535243574661043352422598443323384197261529289829451787586618886007968913414366545291507686451774653217577858375086817168124727394445167274831801876424578654786480330913650363551771258617533162477541882336257099777912519011890593910515860435759936717781, 15567087904962670212229825713697043597876172881256160613623383896576159414077875401117959132252949501643234465895697270909085179587988268864498823765197994781747034644583869111599516151129007414228897958635533561248099927507725880289417298814703767549313482346652043188826434944367260731729064673486516315207, 10757138067445225320504771816863593606847219020279502671965413470243269270456133564739090471033889069283122519782525412134604896073598293410977787230108853737796640474070194546344190858079847734817109910030714675258996740807873872365037296486121580542250452443305370358407408558223735250474249180772656905880, 68097848963949068260912124852455363245291187860801223898468533992003737157497436432969031551088942445561676359631354280979357356539429863946694570097104716411407829017684705171462511875250672979623888463245258237680782731827727876526411531354910982579164963119481534453651300645314177478026462894232377307020]

先通过LCG求出seed,然后剪枝得到p,q。LCG就不过多赘述了

exp.py

Python
from Crypto.Util.number import *
import sys
sys.setrecursionlimit(3000)

gift = 39428646082513135314545544161912595458975375891528176714825766497155482031976852156313956476772023258684487799640179241987139554034654104867011313090105438798561154654679825702410748780286094326639330840289843154525176685892323447168072417654823748596238888125898914210332775882916911771786984574407163323116
N = 24044063028844014127418595700558729326190738802687551098858513077613750188240082663594575453404975706225242363463089392757425008423696150244560748490108425645064339883915929498539109384801415313004805586193044292137299902797522618277016789979196782551492020031695781792205215671106103568559626617762521687128199445018651010056934305055040748892733145467040663073395258760159451903432330506383025685265502086582538667772105057401245864822281535425692919273252955571196166824113519446568745718898654447958192533288063735350717599092500158028352667339959012630051251024677881674246253876293205648190626145653304572328397
c = 14883053247652228283811442762780942186987432684268901119544211089991663825267989728286381980568977804079766160707988623895155236079459150322336701772385709429870215701045797411519212730389048862111088898917402253368572002593328131895422933030329446097639972123501482601377059155708292321789694103528266681104521268192526745361895856566384239849048923482217529011549596939269967690907738755747213669693953769070736092857407573675987242774763239531688324956444305397953424851627349331117467417542814921554060612622936755420459029769026126293588814831034143264949347763031994934813475762839410192390466491651507733968227

## 低位往高位爆
def findp(p,q):
    if len(p) == 1024:
        pp = int(p,2)
        if N % pp == 0:
            print("p = ",pp)
            print("q = ",N // pp)
            qq = N // pp
            d = inverse(65537,(pp-1)*(qq-1))
            m = pow(c,d,N)
            print(long_to_bytes(m))
            return
    else:
        l = len(p)
        pp = int(p,2)
        qq = int(q,2)
        if (pp ^ qq) % (2 ** l) == gift % (2 ** l) and pp * qq % (2 ** l) == N % (2**l):
            findp('1' + p,'1' + q)
            findp('1' + p,'0' + q)
            findp('0' + p,'1' + q)
            findp('0' + p,'0' + q)

findp('1','1')
## flag{y0u_kn0w_Pruning_and_lcg}

2.gift = p ^ (q >> 16)

题目来源:2023DASCTF暑期挑战赛——EzRSA

task.py

Python
from Crypto.Util.number import *
from secret import secret, flag
def encrypt(m):
    return pow(m, e, n)
assert flag == b"dasctf{" + secret + b"}"
e = 11
p = getPrime(512)
q = getPrime(512)
n = p * q
P = getPrime(512)
Q = getPrime(512)
N = P * Q
gift = P ^ (Q >> 16)

print(N, gift, pow(n, e, N))
print(encrypt(bytes_to_long(secret)),
    encrypt(bytes_to_long(flag)))

N = 75000029602085996700582008490482326525611947919932949726582734167668021800854674616074297109962078048435714672088452939300776268788888016125632084529419230038436738761550906906671010312930801751000022200360857089338231002088730471277277319253053479367509575754258003761447489654232217266317081318035524086377
gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034
pow(n,e,N) = 14183763184495367653522884147951054630177015952745593358354098952173965560488104213517563098676028516541915855754066719475487503348914181674929072472238449853082118064823835322313680705889432313419976738694317594843046001448855575986413338142129464525633835911168202553914150009081557835620953018542067857943
pow(secret,e,n) = 69307306970629523181683439240748426263979206546157895088924929426911355406769672385984829784804673821643976780928024209092360092670457978154309402591145689825571209515868435608753923870043647892816574684663993415796465074027369407799009929334083395577490711236614662941070610575313972839165233651342137645009
pow(flag,e,n) = 46997465834324781573963709865566777091686340553483507705539161842460528999282057880362259416654012854237739527277448599755805614622531827257136959664035098209206110290879482726083191005164961200125296999449598766201435057091624225218351537278712880859703730566080874333989361396420522357001928540408351500991

思路很清晰,先分解N,解RSA得到n,再富兰克林相关消息攻击求解flag。

这里只展示剪枝分解N

exp

Python
import sys
from tqdm import *

sys.setrecursionlimit(3000)
gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034
N = 75000029602085996700582008490482326525611947919932949726582734167668021800854674616074297109962078048435714672088452939300776268788888016125632084529419230038436738761550906906671010312930801751000022200360857089338231002088730471277277319253053479367509575754258003761447489654232217266317081318035524086377

## 低位往高位爆
def findp(p,q):
    if len(p) == 512:
        pp = int(p,2)
        if N % pp == 0:
            print("p = ",pp)
            print("q = ",N // pp)

    else:
        l = len(p)
        pp = int(p,2)
        qq = int(q,2)
        if (pp ^ (qq >> 16)) % (2 ** l) == gift % (2 ** l) and pp * qq % (2 ** l) == N % (2 ** l):
            findp('1' + p,'1' + q)
            findp('1' + p,'0' + q)
            findp('0' + p,'1' + q)
            findp('0' + p,'0' + q)

for i in trange(2**16,2**17):
    findp('1',bin(i)[2:])
"""
p =  8006847171912577069085166877758626954304824756138758266557706391662987806065132448544117840031499707938227955094109779732609035310252723066470330862622641
q =  9366986529377069783394625848920106951220134111548343265311677163992169555436421569730703291128771472885865288798344038000984911921843088200997725324682297
"""

3.不会取名字

题目来源:2024HDCTF——ezrsa

task.py

Python
from Crypto.Util.number import *
import random
import hashlib
from secret import flag


gen1 = lambda bits=1024, rand=lambda n: bytes(random.getrandbits(1) for _ in range(n)): (getPrime(bits//2, randfunc=rand), getPrime(bits//2, randfunc=rand))

gen2 = lambda alpha=512, K=500, T=getPrime(506): next(((p, q, T) for q, r in [(getPrime(alpha), getPrime(alpha))] for k in range(2, K+1) if (isPrime(p := r + (k * q - r) % T))), None)

p1,q1=gen1()
p2, q2, T = gen2()
assert flag == "DASCTF{" + hashlib.md5(str(p1 + p2 + q1 + q2).encode()).hexdigest() + "}"
print(p1*q1)
print((p2*q2,T))
## 44945076854246685060397710825960160082061127479194994041436997195972585701097443198954359213635892234058786065342178389181538153413878118039445271277476379366294977408981257175008890470376094381644530106799352839565803317977637572325347776636285703517680754624094985374606187797141657688145287340444623176193
## (57784854392324291351358704449756491526369373648574288191576366413179694041729248864428194536249209588548791706980878177790271653262097539281383559433402738548851606776347237650302071287124974607439996041713554182180186588308614458904981542909792071322939678815174962366963098166320441995961513884899917498099, 150514823288951667574011681197229106951781617714873679347685702558528178681176081082658953342482323349796111911103531429615442550000291753989779754337491)

这里仅对gen1()这部分讲解

gen1(),关键就是把getPrime(bits,randfunc=None)改成了getPrime(bits,randfunc=rand)

我不太清楚randfunc起到什么作用,自己打印几组数据发现,gen1生成的素数每8bit只有2个情况,分别是

00000000 00000001

而高8位必定是10000000

通过这个性质,再利用p * q % 2**l == n % 2**l,写一个剪枝就可以爆出来

exp.py

Python
from tqdm import *

n1 = 44945076854246685060397710825960160082061127479194994041436997195972585701097443198954359213635892234058786065342178389181538153413878118039445271277476379366294977408981257175008890470376094381644530106799352839565803317977637572325347776636285703517680754624094985374606187797141657688145287340444623176193

def find(p,q,l):
    if l == 504:
        pp = int("10000000"+ p,2)
        qq = int("10000000"+ q,2)
        if n1 % pp == 0:
            print(f"p1 = {pp}")
            print(f"q1 = {qq}")
            return 
    else:
        pp = int(p,2)
        qq = int(q,2)
        if pp * qq % 2**l == n1 % 2**l:
            find("00000001" + p,"00000001" + q,l+8)
            find("00000001" + p,"00000000" + q,l+8)
            find("00000000" + p,"00000001" + q,l+8)
            find("00000000" + p,"00000000" + q,l+8)

find("00000001","00000001",8)

4.不会取名字

题目来源:2024osuCTF

task.py

Python
from Crypto.Util.number import isPrime,bytes_to_long
import random
import os

def getWYSIprime():
    while True:
        digits = [random.choice("727") for _ in range(272)]
        prime = int("".join(digits))
        if isPrime(prime):
            return prime

## RSA encryption using the WYSI primes
p = getWYSIprime()
q = getWYSIprime()
n = p * q
e = 65537
flag = bytes_to_long(os.getenv("FLAG",b"osu{fake_flag_for_testing}"))
ciphertext = pow(flag,e,n)
print(f"n = {n}")
print(f"e = {e}")
print(f"ciphertext = {ciphertext}")
"""
n = 2160489795493918825870689458820648828073650907916827108594219132976202835249425984494778310568338106260399032800745421512005980632641226298431130513637640125399673697368934008374907832728004469350033174207285393191694692228748281256956917290437627249889472471749973975591415828107248775449619403563269856991145789325659736854030396401772371148983463743700921913930643887223704115714270634525795771407138067936125866995910432010323584269926871467482064993332990516534083898654487467161183876470821163254662352951613205371404232685831299594035879
e = 65537
ciphertext = 2087465275374927411696643073934443161977332564784688452208874207586196343901447373283939960111955963073429256266959192725814591103495590654238320816453299972810032321690243148092328690893438620034168359613530005646388116690482999620292746246472545500537029353066218068261278475470490922381998208396008297649151265515949490058859271855915806534872788601506545082508028917211992107642670108678400276555889198472686479168292281830557272701569298806067439923555717602352224216701010790924698838402522493324695403237985441044135894549709670322380450
"""

素数由7 or 2组成,所以剪枝就好了,和二进制的01没什么区别

exp.py

Python
from Crypto.Util.number import *
import itertools
import gmpy2

n = 2160489795493918825870689458820648828073650907916827108594219132976202835249425984494778310568338106260399032800745421512005980632641226298431130513637640125399673697368934008374907832728004469350033174207285393191694692228748281256956917290437627249889472471749973975591415828107248775449619403563269856991145789325659736854030396401772371148983463743700921913930643887223704115714270634525795771407138067936125866995910432010323584269926871467482064993332990516534083898654487467161183876470821163254662352951613205371404232685831299594035879
e = 65537
ciphertext = 2087465275374927411696643073934443161977332564784688452208874207586196343901447373283939960111955963073429256266959192725814591103495590654238320816453299972810032321690243148092328690893438620034168359613530005646388116690482999620292746246472545500537029353066218068261278475470490922381998208396008297649151265515949490058859271855915806534872788601506545082508028917211992107642670108678400276555889198472686479168292281830557272701569298806067439923555717602352224216701010790924698838402522493324695403237985441044135894549709670322380450

def findp(p,q):
    l = len(p)
    if l == 272 and n % int(p) == 0:
        q = n // int(p)
        print(f"p = {p}")
        print(f"q = {q}")
    else:
        table = ["2","7"]
        for i,j in itertools.product(table,repeat=2):
            pp = int(i + p)
            qq = int(j + q)
            if pp * qq % 10**l == n % 10**l:
                findp(i+p,j+q)

## findp("7","7")
p = 27777727727777722777277277777272772727772722777777277777277772227772772772227272727777772772772727227772777277727777777222777777277727772272272777772722727722777727277727727777777772772777772777277772222727227777777222727777772772272727777222777777277777727272772272272277
q = 77777772777772222722727222227777272777772777772277727772722777777777227277727272772727277277272772727227272772772222277777772727727772722772777272727777272722772777277777277777277777727777277277777277777272772772777777727772727277727777772772777772272772272222227777777227
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(ciphertext,d,n)
print(long_to_bytes(m))
## osu{h4v3_y0u_3v3r_n0t1c3d_th4t_727_1s_pr1m3?}

这四种其实感觉都算是一个类型的。还有很多类型的剪枝可以参考:Crypto趣题-剪枝 | 糖醋小鸡块的blog (tangcuxiaojikuai.xyz)

5.剪枝高位泄露

改编自2024XYCTF——铜匠——part3

task.py

Python
from Crypto.Util.number import *
from uuid import uuid4

flag = "flag{" + str(uuid4()) + "}"
m = bytes_to_long(flag.encode())

e = 65537
p = getPrime(512)
q = getPrime(512)
n = p * q
n = p * q
c = pow(m,e,n)
hint = (p ^ q) >> 200

print(f"c = {c}")
print(f"hint = {hint}")
print(f"n = {n}")
"""
c = 131369115652458166734046676308463999858958566061027321031978783010359641091133329180071403453658128376834077573719048417128188606423319316139110362434662107918020440100011984923813248515798307240829242781364796147914413916010985480344221078382648887106771447184119879293816004142774768308105423590006218154095
hint = 1418156189500701046118843280016917462701888775284481310064968961841401532967678842766145628051
n = 149674440332297459310104619359509309480607215578927485825557593606756533602713173946403283163260154493438427348209708307170391308942988466700336664636266655214819841710712761463985437035510015805275916279554381867547108324810390610671268389247357987728963839572199589558858271355105026004101888297456720686127
"""

原理参考:2023-鹏城杯-wp-crypto | 糖醋小鸡块的blog (tangcuxiaojikuai.xyz)

exp

Python
from Crypto.Util.number import *

c = 77362292774939745985127248740136531356798680244461718641182309736632869439528624490319564705732587035892746550259989967311639940839352531540590481816143399815777578738222244349541298856970218973629828183402846736572747940317185733010901816452760815595925525717774150109667814870300394441913855689875810419726
hint = 3952699881526141586582096584337236735424573202916336803978009509888494841237961467284222140708
n = 101811981048263163700430054305833461232304464649339483324456752139265661172425623344370861924802283149319747333342558424458871624320190755682987168048547266046556603061770039238687342160962557147928318419071351955573359146111983095312306624896541836196196950684743489047140141567937687816622755267268993977151
e = 65537

leak = hint << 200
leak = bin(leak)[2:].zfill(512)
leakbits = 200                          ## 缺失的位数

def findp(pp,qq,l):
    p_Min = int(pp + (512-l)*"0",2)
    p_Max = int(pp + (512-l)*"1",2)
    q_Min = int(qq + (512-l)*"0",2)
    q_Max = int(qq + (512-l)*"1",2)

    if(l == 512 - leakbits):
        R.<x> = PolynomialRing(Zmod(n))
        phigh = p_Min
        f = phigh + x
        root = f.small_roots(X=2^leakbits, beta=0.499)
        if root != []:
            p = phigh + int(root[0])
            q = n // p
            d = inverse(e,(p-1)*(q-1))
            m = pow(c,d,n)
            print(long_to_bytes(m))

    if (p_Max * q_Max < n) or (p_Min * q_Min > n):
        return

    ## 以下是根据异或来操作的
    if leak[l] == '0':
        findp(pp + '1',qq + '1',l+1)
        findp(pp + '0',qq + '0',l+1)
    if leak[l] == '1':
        findp(pp + '1',qq + '0',l+1)
        findp(pp + '0',qq + '1',l+1)

findp('1','1',1)

二十一、一些分解n常用的方法

1.费马分解

\[ n = p\times q = (\frac{p+q}{2})^2 - (\frac{p-q}{2})^2 \]

简单来说,费马分解干了这样一件事:

\(a = \sqrt{n}\)开始,逐步增加,直到发现\(a^2 - n\)是一个平方数,就找到了正确的\(a,b\),然后就可以计算\(p = a +b ,q = a -b\)

从这个思想可以发现,这个算法的效率取决于\(\frac{p+q}{2}\)\(\sqrt{n}\)的差距,所以当\(p,q\)接近的时候,用费马分解可以很快计算出\(p,q\)

这里给出一个脚本

Python
from math import isqrt

def fermat(n):
    a = isqrt(n)
    b2 = a * a - n
    b = isqrt(n)
    count = 0
    while b * b != b2:
        a = a + 1
        b2 = a * a - n
        b = isqrt(b2)
        count += 1
    p = a + b
    q = a - b
    assert n == p * q
    return p, q

例题——MoeCTF2023 |p-q|

task.py

Python
with open("flag.txt","rb") as fs:
    flag = fs.read().strip()
assert len(flag) == 72

m = int.from_bytes(flag,"big")

from Crypto.Util.number import getPrime, isPrime

def next_prime(p):
    while True:
        p += 2
        if isPrime(p):
            return p

p = getPrime(2048)
q = next_prime(p)
n = p * q
e = 65537
c = pow(m,e,n)
print("n =",n)
print("c =",c)

## n = 329960318345010350458589325571454799968957932130539403944044204698872359769449414256378111233592533561892402020955736786563103586897940757198920737583107357264433730515123570697570757034221232010688796344257587359198400915567115397034901247038275403825404094129637119512164953012131445747740645183682571690806238508035172474685818036517880994658466362305677430221344381425792427288500814551334928982040579744048907401043058567486871621293983772331951723963911377839286050368715384227640638031857101612517441295926821712605955984000617738833973829140899288164786111118033301974794123637285172303688427806450817155786233788027512244397952849209700013205803489334055814513866650854230478124920442832221946442593769555237909177172933634236392800414176981780444770542047378630756636857018730168151824307814244094763132088236333995807013617801783919113541391133267230410179444855465611792191833319172887852945902960736744468250550722314565805440432977225703650102517531531476188269635151281661081058374242768608270563131619806585194608795817118466680430500830137335634289617464844004904410907221482919453859885955054140320857757297655475489972268282336250384384926216818756762307686391740965586168590784252524275489515352125321398406426217
## c = 307746143297103281117512771170735061509547958991947416701685589829711285274762039205145422734327595082350457374530975854337055433998982493020603245187129916580627539476324521854057990929173492940833073106540441902619425074887573232779899379436737429823569006431370954961865581168635086246592539153824456681688944066925973182272443586463636373955966146029489121226571408532284480270826510961605206483011204059402338926815599691009406841471142048842308786000059979977645988396524814553253493672729395573658564825709547262230219183672493306100392069182994445509803952976016630731417479238769736432223194249245020320183199001774879893442186017555682902409661647546547835345461056900610391514595370600575845979413984555709077635397717741521573798309855584473259503981955303774208127361309229536010653615696850725905168242705387575720694946072789441481191449772933265705810128547553027708513478130258801233619669699177901566688737559102165508239876805822898509541232565766265491283807922473440397456701500524925191214292669986798631732639221198138026031561329502985577205314190565609214349344303324429408234237832110076900414483795318189628198913032900272406887003325858236057373096880675754802725017537119549989304878960436575670784578550

这里p,q是相邻的素数,可以直接开根号分解,也可以用费马分解

exp.py

Python
from math import isqrt
from Crypto.Util.number import *

def fermat(n):
    a = isqrt(n)
    b2 = a * a - n
    b = isqrt(n)
    count = 0
    while b * b != b2:
        a = a + 1
        b2 = a * a - n
        b = isqrt(b2)
        count += 1
    p = a + b
    q = a - b
    assert n == p * q
    return p, q

n = 
c = 
p,q = fermat(n)
d = inverse(65537,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))

2.p-1光滑

原理没了解,就存个脚本吧

脚本来源:RSA攻击:Smooth攻击_p-1光滑攻击-CSDN博客

Python
import gmpy2

def Pollards_p_1(N):
    a = 2   ## 为了快速计算以及满足费马小定理条件
    n = 2   ## 从1开始没必要

    while True:
        a = pow(a, n, N)    ## 递推计算a^B!
        res = gmpy2.gcd(a - 1, N)   ## 尝试计算p
        if res != 1 and res != N:   ## 满足条件后返回
            return res
        n += 1

例题——NCTF2019 childRSA

task.py

Python
from random import choice
from Crypto.Util.number import isPrime, sieve_base as primes
from flag import flag


def getPrime(bits):
    while True:
        n = 2
        while n.bit_length() < bits:
            n *= choice(primes)
        if isPrime(n + 1):
            return n + 1

e = 0x10001
m = int.from_bytes(flag.encode(), 'big')
p, q = [getPrime(2048) for _ in range(2)]
n = p * q
c = pow(m, e, n)

## n = 32849718197337581823002243717057659218502519004386996660885100592872201948834155543125924395614928962750579667346279456710633774501407292473006312537723894221717638059058796679686953564471994009285384798450493756900459225040360430847240975678450171551048783818642467506711424027848778367427338647282428667393241157151675410661015044633282064056800913282016363415202171926089293431012379261585078566301060173689328363696699811123592090204578098276704877408688525618732848817623879899628629300385790344366046641825507767709276622692835393219811283244303899850483748651722336996164724553364097066493953127153066970594638491950199605713033004684970381605908909693802373826516622872100822213645899846325022476318425889580091613323747640467299866189070780620292627043349618839126919699862580579994887507733838561768581933029077488033326056066378869170169389819542928899483936705521710423905128732013121538495096959944889076705471928490092476616709838980562233255542325528398956185421193665359897664110835645928646616337700617883946369110702443135980068553511927115723157704586595844927607636003501038871748639417378062348085980873502535098755568810971926925447913858894180171498580131088992227637341857123607600275137768132347158657063692388249513
## c = 26308018356739853895382240109968894175166731283702927002165268998773708335216338997058314157717147131083296551313334042509806229853341488461087009955203854253313827608275460592785607739091992591431080342664081962030557042784864074533380701014585315663218783130162376176094773010478159362434331787279303302718098735574605469803801873109982473258207444342330633191849040553550708886593340770753064322410889048135425025715982196600650740987076486540674090923181664281515197679745907830107684777248532278645343716263686014941081417914622724906314960249945105011301731247324601620886782967217339340393853616450077105125391982689986178342417223392217085276465471102737594719932347242482670320801063191869471318313514407997326350065187904154229557706351355052446027159972546737213451422978211055778164578782156428466626894026103053360431281644645515155471301826844754338802352846095293421718249819728205538534652212984831283642472071669494851823123552827380737798609829706225744376667082534026874483482483127491533474306552210039386256062116345785870668331513725792053302188276682550672663353937781055621860101624242216671635824311412793495965628876036344731733142759495348248970313655381407241457118743532311394697763283681852908564387282605279108

exp.py

Python
import gmpy2
from Crypto.Util.number import *

def Pollards_p_1(N):
    a = 2   ## 为了快速计算以及满足费马小定理条件
    n = 2   ## 从1开始没必要

    while True:
        a = pow(a, n, N)    ## 递推计算a^B!
        res = gmpy2.gcd(a - 1, N)   ## 尝试计算p
        if res != 1 and res != N:   ## 满足条件后返回
            return res
        n += 1

n = 
c = 
p = Pollards_p_1(n)
q = n // p
d = inverse(65537,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(m))
## NCTF{Th3r3_ar3_1ns3cure_RSA_m0duli_7hat_at_f1rst_gl4nce_appe4r_t0_be_s3cur3}

3.p+1光滑

脚本来源:

Python
def lucas(c, d, N):
    x = c ## a1
    y = (c**2 - 2) % N ## a2
    for bit in bin(d)[3:]: ## 快速乘(从高到低位)--我个人理解
        if bit == '1':
            x = (x*y - c) % N ## 下标对应a_{x+y},其次保证a_{2k-1}=a_{k}a_{k-1}-a_{0}成立
            y = (y**2 - 2) % N ## 使得y翻倍--正常的快速幂流程
        else:
            y = (x*y - c) % N ## 保证a_{2k-1}=a_{k}a_{k-1}-a_{0}成立
            x = (x**2 - 2) % N ## a_{k} 翻倍
    return x #返回a_{ed}

才疏学浅,没找到例题

4.已知phi和n分解n

如果是两个因子的,\(n = p\times q\),那么直接联立方程就可以解

如果是多因子的话,就没这么容易了,以NKCTF2023的ezRSA为例

例题——NKCTF2023 ezRSA

task.py

Python
from Crypto.Util.number import *
from secret import flag

m1 = bytes_to_long(flag[:len(flag)//3])
m2 = bytes_to_long(flag[len(flag)//3:])

def gen():
    prime_list  = []
    for i in range(4):
        prime_list.append(getPrime(512))
    return sorted(prime_list)

prime_list = gen()
p,q,r,t = prime_list[0],prime_list[3],prime_list[1],prime_list[2]
e = 65537
n = p*q*r*t
phi = (p-1)*(q-1)*(r-1)*(t-1)
c1 = pow(m1,e,p*q)
p1 = getPrime(512)
q1 = getPrime(512)
N = p1*q1
c2 = pow(m2,p1,N)
c3 = pow(m2,q1,N)
print(f'n = {n}')
print(f'phi = {phi}')
print(f'c1 = {c1}')
print(f'N = {N}')
print(f'c2 = {c2}')
print(f'c3 = {c3}')

'''
n = 8836130216343708623415307573630337110573363595188748983290313549413242332143945452914800845282478216810685733227137911630239808895196748125078747600505626165666334675100147790578546682128517668100858766784733351894480181877144793496927464058323582165412552970999921215333509253052644024478417393146000490808639363681195799826541558906527985336104761974023394438549055804234997654701266967731137282297623426318212701157416397999108259257077847307874122736921265599854976855949680133804464839768470200425669609996841568545945133611190979810786943246285103031363790663362165522662820344917056587244701635831061853354597
phi = 8836130216343708623415307573630337110573363595188748983290313549413242332143945452914800845282478216810685733227137911630239808895196748125078747600505622503351461565956106005118029537938273153581675065762015952483687057805462728186901563990429998916382820576211887477098611684072561849314986341226981300596338314989867731725668312057134075244816223120038573374383949718714549930261073576391501671722900294331289082826058292599838631513746370889828026039555245672195833927609280773258978856664434349221972568651378808050580665443131001632395175205804045958846124475183825589672204752895252723130454951830966138888560
c1 = 78327207863361017953496121356221173288422862370301396867341957979087627011991738176024643637029313969241151622985226595093079857523487726626882109114134910056673489916408854152274726721451884257677533593174371742411008169082367666168983943358876017521749198218529804830864940274185360506199116451280975188409
N = 157202814866563156513184271957553223260772141845129283711146204376449001653397810781717934720804041916333174673656579086498762693983380365527400604554663873045166444369504886603233275868192688995284322277504050322927511160583280269073338415758019142878016084536129741435221345599028001581385308324407324725353
c2 = 63355788175487221030596314921407476078592001060627033831694843409637965350474955727383434406640075122932939559532216639739294413008164038257338675094324172634789610307227365830016457714456293397466445820352804725466971828172010276387616894829328491068298742711984800900411277550023220538443014162710037992032
c3 = 9266334096866207047544089419994475379619964393206968260875878305040712629590906330073542575719856965053269812924808810766674072615270535207284077081944428011398767330973702305174973148018082513467080087706443512285098600431136743009829009567065760786940706627087366702015319792328141978938111501345426931078
'''

exp.py

Python
from math import gcd
from math import isqrt
from random import randrange
from gmpy2 import is_prime


def factorize(N, phi):
    """
    Recovers the prime factors from a modulus if Euler's totient is known.
    This method only works for a modulus consisting of 2 primes!
    :param N: the modulus
    :param phi: Euler's totient, the order of the multiplicative group modulo N
    :return: a tuple containing the prime factors, or None if the factors were not found
    """
    s = N + 1 - phi
    d = s ** 2 - 4 * N
    p = int(s - isqrt(d)) // 2
    q = int(s + isqrt(d)) // 2
    return p, q


def factorize_multi_prime(N, phi):
    """
    Recovers the prime factors from a modulus if Euler's totient is known.
    This method works for a modulus consisting of any number of primes, but is considerably be slower than factorize.
    More information: Hinek M. J., Low M. K., Teske E., "On Some Attacks on Multi-prime RSA" (Section 3)
    :param N: the modulus
    :param phi: Euler's totient, the order of the multiplicative group modulo N
    :return: a tuple containing the prime factors
    """
    prime_factors = set()
    factors = [N]
    while len(factors) > 0:
        ## Element to factorize.
        N = factors[0]

        w = randrange(2, N - 1)
        i = 1
        while phi % (2 ** i) == 0:
            sqrt_1 = pow(w, phi // (2 ** i), N)
            if sqrt_1 > 1 and sqrt_1 != N - 1:
                ## We can remove the element to factorize now, because we have a factorization.
                factors = factors[1:]

                p = gcd(N, sqrt_1 + 1)
                q = N // p

                if is_prime(p):
                    prime_factors.add(p)
                elif p > 1:
                    factors.append(p)

                if is_prime(q):
                    prime_factors.add(q)
                elif q > 1:
                    factors.append(q)

                ## Continue in the outer loop
                break

            i += 1

    return tuple(prime_factors)

n = 
phi = 
prime_list = sorted(factorize_multi_prime(n,phi))
p,q,r,s = prime_list[0],prime_list[3],prime_list[1],prime_list[2]

5.已知e,d分解n

Python
import random
import gmpy2

def divide_pq(e, d, n):
    k = e*d - 1
    while True:
        g = random.randint(2, n-1)
        t = k
        while True:
            if t % 2 != 0:
                break
            t //= 2
            x = pow(g, t, n)
            if x > 1 and gmpy2.gcd(x-1, n) > 1:
                p = gmpy2.gcd(x-1, n)
                return (p, n//p)

二十二、不知道叫什么分类的题目

给出p + q低位

题目改编自2024XYCTF——铜匠——part2

task.py

Python
from Crypto.Util.number import *
from uuid import uuid4

flag = "flag{" + str(uuid4()) + "}"
m = bytes_to_long(flag.encode())

e = 65537
p = getPrime(1024)
q = getPrime(1024)
n = p * q

c = pow(m, e, n)
hint = (p + q) & ((1 << 624) - 1)
print(f"c = {c}")
print(f"hint = {hint}")
print(f"n = {n}")
"""
c = 15315623227108436291858457034857784719507552879018189851637500530704870673436079954784519165863568569923075589417879105802430194799540543536178163710092884414704414320471378011034947694757321045648672645088739109820484000260862752393231610482665197130322211444656668269314857325584729333799747940039582233403795688967132390847508527859286606080564369696319857802230684766189133382904414261588681956483620140766886760632093938290509495921191051004955960321224324525865371027175220422824090922909186107029324403060456677201122370380708945945621513853659281202197178155661602947407740113932985040400431009519035927170893
hint = 24765551240982534917083490896297931130347202481167225145278716583234238038819431100154290815134734511033735464993876484465992525732351399544319245104113383110187111270727627683278542339074
n = 17551595965847360101001559529386245228497232313421489853434901311116953087050954433040038840788997073271446971526589676430781788129026667353632541062149232552031818920006832783397173014011531311807992794738323157382559849771809561200626834225824761208921248849766951980339089458227127422515917026821919873970760316856393212652571020677585627719682906485373773819364730302897222175047226443095023421963275036185038777190349527562337069435775581997139041749482233185668100893166822768561406331106814954573421744711888039361670837655170604347860448702902142977569791794645486978083071644858164529311966931143494605467581
"""

由题意知\(hint \equiv p+q \mod 2^{624}\)

除此之外还可以得到\(n_{low} \equiv p\times q \mod 2^{624}\)

于是有\(n_{low} \equiv p(hint - p) \mod 2^{624}\),解这个方程,然后得到p的低位,再用coppersmith

exp.sage

Python
from Crypto.Util.number import *

c = 
hint = 
n = 
e = 65537


n_low = n % 2^624
var('p')
f = p * (hint - p) - n_low
res = solve_mod(f,2^624)
for i in res:
    plow = int(i[0])
    R.<x> = PolynomialRing(Zmod(n))
    f2 = x * 2^624 + plow
    f2 = f2.monic()
    root = f2.small_roots(X = 2^400,beta=0.4)
    if root != []:
        p = int(root[0])*2^624 + plow
        q = n // p
        d = inverse(e,(p-1)*(q-1))
        m = pow(c,d,n)
        print(long_to_bytes(m))

给出p + q高位

题目改编自2023贵阳大数据安全精英赛——childrsa

task.py

Python
from Crypto.Util.number import *
from uuid import uuid4

flag = "flag{" + str(uuid4()) + "}"
m = bytes_to_long(flag.encode())

e = 65537
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(m,e,n)
hint = (p + q) >> 400

print(f"c = {c}")
print(f"hint = {hint}")
print(f"n = {n}")
"""
c = 9968814618309288374765866491408679544255429893255485341540355195679112942954793887247314436025657932769746362042585414910893400260770578028623836705800055045072907439444477587736093915786634848757052762926680474130492661750432044025559425178180215747669943104929529122332455902347039817592660193268467698523220383789551009222998925658852390205657636722754375420684776093364112335355059660951403603124371637134722485720098467185512008162108300224681045740170399118079432119176169258163078208915377786922862558709935512780853980376778422910630993460815435812492821018759585591884031366023668940372754253922297009332299
hint = 105339843160443546161059100165057877716740618426298713183228594499797346255161510101279378179555206038228997256205578377516899889823785206924887213498045434452190147663923551839494492842528
n = 18082068933780051996564597024915927694586996785675007728934595064379707482865414885175435323040864143693167921685287351955951750372213399691641808639756160589090710830417826035004009898533761679036070970158874299127191974011618903726273713888134189317545858033157115955525192756551460792306555827664839412205373639439765940037587708927072607585261515428021124875200073775443189376478576573477677951404802079319347815808709279487553524912259078196698834492416001870786404364835221144821510700252279703537618579227763205158431798627532437072037863851605556234186314341793234064284447561153310642226744523000217117902339
"""

我们可以联立\(p+q\)的高位和\(n = p \times q\)解方程组,此时解出来的\(p\)和正确的\(p\)高位是一样的,最后再用coppersmith去解低400位

实际测试发现,解方程可能存在误差,所以最后用copper处理低405位

Exp

Python
from Crypto.Util.number import *

c = 
hint = 
n = 
e = 65537

R.<x> = PolynomialRing(RealField(2048))

f = x * ((hint << 400) - x) - n
res = f.roots()
for root in res:
    phigh = int(root[0]) >> 405 << 405
    PR.<x> = PolynomialRing(Zmod(n))
    f1 = phigh + x
    res1 = f1.small_roots(X = 2^405,beta=0.4)
    if res1 != []:
        p = phigh + int(res1[0])
        q = n // p
        d = inverse(e,(p-1)*(q-1))
        m = pow(c,d,n)
        print(long_to_bytes(m))

已知p^2 + q^2分解n = p*q

利用sagemath自带函数two_squares(),甚至有three_squares()

二十三、私钥文件提取参数

常考的就是PKCS1形式的私钥文件,参考:参考文章

先生成私钥文件

Python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA

p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
d = inverse(e,(p-1)*(q-1))

print(f"n = {n}")
print(f"e = {e}")
print(f"p = {p}")
print(f"q = {q}")
print(f"dp = {d % (p-1)}")
print(f"dq = {d % (q-1)}")
print(f"inv = {inverse(p,q)}")

privatekey = RSA.construct((n,e,int(d)))

with open("privatekey.pem","wb") as f2:
    f2.write(privatekey.export_key())

得到

Python
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAhgPB1hHIr7An3HDG1dqJo8UQu+5oOLOA119z0rZESozrr1ox
3g0Ku86t1X1OpbawWr8cFzXgYsDtgZPWhEExbsU/qHGZXewo8ofe/4Sa/s1DOpUm
ZIFsM/YAtWEex+aTwA9FT+6EZ3OYJatmY9iq9ewnAjeCdBwll04C8vT9Ke73Xndn
B8tTtL2S4hIcuz7vVd1HTZSfQ+ousLUM5OuTe0361NFqp4RFhhHjtM9cuvyaPaMF
vRFoemXyCH8pfxgl4llFSBJitPAeLBxHpV9PjlLBp4kYIzPrYDMp1AF2seQyvb8W
NujFcu0ZvOZRuHxULiUT2RI2srZ6eyAoq7RXiwIDAQABAoIBAE/yw9tqZpfw9ga7
PNNteTk7Ih2LP5+77nwN1LH6zEjRQvUsUJ2QmDusM+YtyBJyJ0krw51RJdikEcyA
nrPtlIjpoW1iv8TZUyBE0FMND848NAQp4GqLDzr8YjXSh6NnufMU6RujRlfVDQpD
82RTaMInLKpU5T1RYVefUYERiEpQCJjzDd2fb1UlCJx6XZguTXTsqCmhr7Lr6xN1
WvSdTY2a7cPxnusggrCaiYxoCjJQRPl+Z4O33gHkXNbZnch5YlEQui1+fanofKuk
lK3mAbqBwo/lfXXAu5GbhMdIZ6Iw68vAiFJQtuwvOYE1h6sN3EHsjnfkbfgC+uu4
1QQfAvECgYEAyBFidz8w3ijHW+XDcH20JhHxEMAmFTUf0fXI11x3ECk+6c2yAH1v
DHM4rr3KHspVhEJzyNVsVBg1I4JF5gSkPWsygjdoyMPqjQ9M0YP2Qho6iiiz20c3
w+K9cBezTNtDn+N/gqOrJLBGOpru1zwpfroS4ie76QNpAUZ83OQUVrkCgYEAq3sG
B0rZvn3+LbQ3S/AlydpnZYf6e85FUt3fQukRBI4vLl6SpOMJSZfQQUnM+P09t2z7
rH3w4QLOYbfEndt1I/MMhfWb7a96I/Cfw9mW48RIUrk4MUtV/Pq7bKz0vzq/DBnA
44nAsa46bb9ysjJ6+fcnLWNLWz4O9+vBeFaGPmMCgYB+vnSwsrmUpCTX1REhPKFZ
1NfxEqmNmeAUtS6NMKjE9jxDBeqUYOJu8regC9/17ZyLc0XCn2JHTCat3iPF+n7J
4hVXZR7ewS7gOiciPAVQDymyyOJYMh/j2srELl+KewW2TvtCmckcLwfurKROenCX
Ne4sk5t5nI1zH2KO1XcFEQKBgH9DeD/lPyBu5TsKKpfDDGh4HJBvkGhdt3k+jLld
u3GEDGP/cBnLHVNuxfIOUX7ggvMkgMuNVD3KFVzUQ6lb+93IPZ0VoLmPp7gQlqGF
VMSJIZuzNo7u+Ewd0QdgfOuHL85NNqgnzciQI3DbysWRTU9CK+M1c/GtZvJ8F0O2
Az89AoGAILpBAbTHYaBpdXrN4axCm9wbJzEJlk0Pv9BvNXly5LiU/xJsgppP/gmP
9NmUN8QKmp3dmxM9Ub36DYGpZRyPKbKfjKQfSraLXwokr7aIm4jc7Ow0NBBgTsfS
EaqTgYiY9axcqaYS9I5iyfQZt9Kl/hJX6aT9s2M+DQfchzaFWkQ=
-----END RSA PRIVATE KEY-----

解base64再转16进制得到下面的内容

Text Only
30 82 04 a2 02 01 00 02 82 01 01 00 86 03 c1 d6 11 c8 af b0 27 dc 70 c6 d5 da 89 a3 c5 10 bb ee 68 38 b3 80 d7 5f 73 d2 b6 44 4a 8c eb af 5a 31 de 0d 0a bb ce ad d5 7d 4e a5 b6 b0 5a bf 1c 17 35 e0 62 c0 ed 81 93 d6 84 41 31 6e c5 3f a8 71 99 5d ec 28 f2 87 de ff 84 9a fe cd 43 3a 95 26 64 81 6c 33 f6 00 b5 61 1e c7 e6 93 c0 0f 45 4f ee 84 67 73 98 25 ab 66 63 d8 aa f5 ec 27 02 37 82 74 1c 25 97 4e 02 f2 f4 fd 29 ee f7 5e 77 67 07 cb 53 b4 bd 92 e2 12 1c bb 3e ef 55 dd 47 4d 94 9f 43 ea 2e b0 b5 0c e4 eb 93 7b 4d fa d4 d1 6a a7 84 45 86 11 e3 b4 cf 5c ba fc 9a 3d a3 05 bd 11 68 7a 65 f2 08 7f 29 7f 18 25 e2 59 45 48 12 62 b4 f0 1e 2c 1c 47 a5 5f 4f 8e 52 c1 a7 89 18 23 33 eb 60 33 29 d4 01 76 b1 e4 32 bd bf 16 36 e8 c5 72 ed 19 bc e6 51 b8 7c 54 2e 25 13 d9 12 36 b2 b6 7a 7b 20 28 ab b4 57 8b 02 03 01 00 01 02 82 01 00 4f f2 c3 db 6a 66 97 f0 f6 06 bb 3c d3 6d 79 39 3b 22 1d 8b 3f 9f bb ee 7c 0d d4 b1 fa cc 48 d1 42 f5 2c 50 9d 90 98 3b ac 33 e6 2d c8 12 72 27 49 2b c3 9d 51 25 d8 a4 11 cc 80 9e b3 ed 94 88 e9 a1 6d 62 bf c4 d9 53 20 44 d0 53 0d 0f ce 3c 34 04 29 e0 6a 8b 0f 3a fc 62 35 d2 87 a3 67 b9 f3 14 e9 1b a3 46 57 d5 0d 0a 43 f3 64 53 68 c2 27 2c aa 54 e5 3d 51 61 57 9f 51 81 11 88 4a 50 08 98 f3 0d dd 9f 6f 55 25 08 9c 7a 5d 98 2e 4d 74 ec a8 29 a1 af b2 eb eb 13 75 5a f4 9d 4d 8d 9a ed c3 f1 9e eb 20 82 b0 9a 89 8c 68 0a 32 50 44 f9 7e 67 83 b7 de 01 e4 5c d6 d9 9d c8 79 62 51 10 ba 2d 7e 7d a9 e8 7c ab a4 94 ad e6 01 ba 81 c2 8f e5 7d 75 c0 bb 91 9b 84 c7 48 67 a2 30 eb cb c0 88 52 50 b6 ec 2f 39 81 35 87 ab 0d dc 41 ec 8e 77 e4 6d f8 02 fa eb b8 d5 04 1f 02 f1 02 81 81 00 c8 11 62 77 3f 30 de 28 c7 5b e5 c3 70 7d b4 26 11 f1 10 c0 26 15 35 1f d1 f5 c8 d7 5c 77 10 29 3e e9 cd b2 00 7d 6f 0c 73 38 ae bd ca 1e ca 55 84 42 73 c8 d5 6c 54 18 35 23 82 45 e6 04 a4 3d 6b 32 82 37 68 c8 c3 ea 8d 0f 4c d1 83 f6 42 1a 3a 8a 28 b3 db 47 37 c3 e2 bd 70 17 b3 4c db 43 9f e3 7f 82 a3 ab 24 b0 46 3a 9a ee d7 3c 29 7e ba 12 e2 27 bb e9 03 69 01 46 7c dc e4 14 56 b9 02 81 81 00 ab 7b 06 07 4a d9 be 7d fe 2d b4 37 4b f0 25 c9 da 67 65 87 fa 7b ce 45 52 dd df 42 e9 11 04 8e 2f 2e 5e 92 a4 e3 09 49 97 d0 41 49 cc f8 fd 3d b7 6c fb ac 7d f0 e1 02 ce 61 b7 c4 9d db 75 23 f3 0c 85 f5 9b ed af 7a 23 f0 9f c3 d9 96 e3 c4 48 52 b9 38 31 4b 55 fc fa bb 6c ac f4 bf 3a bf 0c 19 c0 e3 89 c0 b1 ae 3a 6d bf 72 b2 32 7a f9 f7 27 2d 63 4b 5b 3e 0e f7 eb c1 78 56 86 3e 63 02 81 80 7e be 74 b0 b2 b9 94 a4 24 d7 d5 11 21 3c a1 59 d4 d7 f1 12 a9 8d 99 e0 14 b5 2e 8d 30 a8 c4 f6 3c 43 05 ea 94 60 e2 6e f2 b7 a0 0b df f5 ed 9c 8b 73 45 c2 9f 62 47 4c 26 ad de 23 c5 fa 7e c9 e2 15 57 65 1e de c1 2e e0 3a 27 22 3c 05 50 0f 29 b2 c8 e2 58 32 1f e3 da ca c4 2e 5f 8a 7b 05 b6 4e fb 42 99 c9 1c 2f 07 ee ac a4 4e 7a 70 97 35 ee 2c 93 9b 79 9c 8d 73 1f 62 8e d5 77 05 11 02 81 80 7f 43 78 3f e5 3f 20 6e e5 3b 0a 2a 97 c3 0c 68 78 1c 90 6f 90 68 5d b7 79 3e 8c b9 5d bb 71 84 0c 63 ff 70 19 cb 1d 53 6e c5 f2 0e 51 7e e0 82 f3 24 80 cb 8d 54 3d ca 15 5c d4 43 a9 5b fb dd c8 3d 9d 15 a0 b9 8f a7 b8 10 96 a1 85 54 c4 89 21 9b b3 36 8e ee f8 4c 1d d1 07 60 7c eb 87 2f ce 4d 36 a8 27 cd c8 90 23 70 db ca c5 91 4d 4f 42 2b e3 35 73 f1 ad 66 f2 7c 17 43 b6 03 3f 3d 02 81 80 20 ba 41 01 b4 c7 61 a0 69 75 7a cd e1 ac 42 9b dc 1b 27 31 09 96 4d 0f bf d0 6f 35 79 72 e4 b8 94 ff 12 6c 82 9a 4f fe 09 8f f4 d9 94 37 c4 0a 9a 9d dd 9b 13 3d 51 bd fa 0d 81 a9 65 1c 8f 29 b2 9f 8c a4 1f 4a b6 8b 5f 0a 24 af b6 88 9b 88 dc ec ec 34 34 10 60 4e c7 d2 11 aa 93 81 88 98 f5 ac 5c a9 a6 12 f4 8e 62 c9 f4 19 b7 d2 a5 fe 12 57 e9 a4 fd b3 63 3e 0d 07 dc 87 36 85 5a 44

对其格式化

Text Only
30 82 04 a2 
    02 01 
        00 

    02 82 01 01 
        00 86 03 c1 d6 11 c8 af b0 27 dc 70 c6 d5 da 89 a3 c5 10 bb ee 68 38 b3 80 d7 5f 73 d2 b6 44 4a 8c eb af 5a 31 de 0d 0a bb ce ad d5 7d 4e a5 b6 b0 5a bf 1c 17 35 e0 62 c0 ed 81 93 d6 84 41 31 6e c5 3f a8 71 99 5d ec 28 f2 87 de ff 84 9a fe cd 43 3a 95 26 64 81 6c 33 f6 00 b5 61 1e c7 e6 93 c0 0f 45 4f ee 84 67 73 98 25 ab 66 63 d8 aa f5 ec 27 02 37 82 74 1c 25 97 4e 02 f2 f4 fd 29 ee f7 5e 77 67 07 cb 53 b4 bd 92 e2 12 1c bb 3e ef 55 dd 47 4d 94 9f 43 ea 2e b0 b5 0c e4 eb 93 7b 4d fa d4 d1 6a a7 84 45 86 11 e3 b4 cf 5c ba fc 9a 3d a3 05 bd 11 68 7a 65 f2 08 7f 29 7f 18 25 e2 59 45 48 12 62 b4 f0 1e 2c 1c 47 a5 5f 4f 8e 52 c1 a7 89 18 23 33 eb 60 33 29 d4 01 76 b1 e4 32 bd bf 16 36 e8 c5 72 ed 19 bc e6 51 b8 7c 54 2e 25 13 d9 12 36 b2 b6 7a 7b 20 28 ab b4 57 8b 

    02 03 
        01 00 01 

    02 82 01 00 
        4f f2 c3 db 6a 66 97 f0 f6 06 bb 3c d3 6d 79 39 3b 22 1d 8b 3f 9f bb ee 7c 0d d4 b1 fa cc 48 d1 42 f5 2c 50 9d 90 98 3b ac 33 e6 2d c8 12 72 27 49 2b c3 9d 51 25 d8 a4 11 cc 80 9e b3 ed 94 88 e9 a1 6d 62 bf c4 d9 53 20 44 d0 53 0d 0f ce 3c 34 04 29 e0 6a 8b 0f 3a fc 62 35 d2 87 a3 67 b9 f3 14 e9 1b a3 46 57 d5 0d 0a 43 f3 64 53 68 c2 27 2c aa 54 e5 3d 51 61 57 9f 51 81 11 88 4a 50 08 98 f3 0d dd 9f 6f 55 25 08 9c 7a 5d 98 2e 4d 74 ec a8 29 a1 af b2 eb eb 13 75 5a f4 9d 4d 8d 9a ed c3 f1 9e eb 20 82 b0 9a 89 8c 68 0a 32 50 44 f9 7e 67 83 b7 de 01 e4 5c d6 d9 9d c8 79 62 51 10 ba 2d 7e 7d a9 e8 7c ab a4 94 ad e6 01 ba 81 c2 8f e5 7d 75 c0 bb 91 9b 84 c7 48 67 a2 30 eb cb c0 88 52 50 b6 ec 2f 39 81 35 87 ab 0d dc 41 ec 8e 77 e4 6d f8 02 fa eb b8 d5 04 1f 02 f1 

    02 81 81 00 
        c8 11 62 77 3f 30 de 28 c7 5b e5 c3 70 7d b4 26 11 f1 10 c0 26 15 35 1f d1 f5 c8 d7 5c 77 10 29 3e e9 cd b2 00 7d 6f 0c 73 38 ae bd ca 1e ca 55 84 42 73 c8 d5 6c 54 18 35 23 82 45 e6 04 a4 3d 6b 32 82 37 68 c8 c3 ea 8d 0f 4c d1 83 f6 42 1a 3a 8a 28 b3 db 47 37 c3 e2 bd 70 17 b3 4c db 43 9f e3 7f 82 a3 ab 24 b0 46 3a 9a ee d7 3c 29 7e ba 12 e2 27 bb e9 03 69 01 46 7c dc e4 14 56 b9 

    02 81 81 00 
        ab 7b 06 07 4a d9 be 7d fe 2d b4 37 4b f0 25 c9 da 67 65 87 fa 7b ce 45 52 dd df 42 e9 11 04 8e 2f 2e 5e 92 a4 e3 09 49 97 d0 41 49 cc f8 fd 3d b7 6c fb ac 7d f0 e1 02 ce 61 b7 c4 9d db 75 23 f3 0c 85 f5 9b ed af 7a 23 f0 9f c3 d9 96 e3 c4 48 52 b9 38 31 4b 55 fc fa bb 6c ac f4 bf 3a bf 0c 19 c0 e3 89 c0 b1 ae 3a 6d bf 72 b2 32 7a f9 f7 27 2d 63 4b 5b 3e 0e f7 eb c1 78 56 86 3e 63 

    02 81 80 
        7e be 74 b0 b2 b9 94 a4 24 d7 d5 11 21 3c a1 59 d4 d7 f1 12 a9 8d 99 e0 14 b5 2e 8d 30 a8 c4 f6 3c 43 05 ea 94 60 e2 6e f2 b7 a0 0b df f5 ed 9c 8b 73 45 c2 9f 62 47 4c 26 ad de 23 c5 fa 7e c9 e2 15 57 65 1e de c1 2e e0 3a 27 22 3c 05 50 0f 29 b2 c8 e2 58 32 1f e3 da ca c4 2e 5f 8a 7b 05 b6 4e fb 42 99 c9 1c 2f 07 ee ac a4 4e 7a 70 97 35 ee 2c 93 9b 79 9c 8d 73 1f 62 8e d5 77 05 11 

    02 81 80 
        7f 43 78 3f e5 3f 20 6e e5 3b 0a 2a 97 c3 0c 68 78 1c 90 6f 90 68 5d b7 79 3e 8c b9 5d bb 71 84 0c 63 ff 70 19 cb 1d 53 6e c5 f2 0e 51 7e e0 82 f3 24 80 cb 8d 54 3d ca 15 5c d4 43 a9 5b fb dd c8 3d 9d 15 a0 b9 8f a7 b8 10 96 a1 85 54 c4 89 21 9b b3 36 8e ee f8 4c 1d d1 07 60 7c eb 87 2f ce 4d 36 a8 27 cd c8 90 23 70 db ca c5 91 4d 4f 42 2b e3 35 73 f1 ad 66 f2 7c 17 43 b6 03 3f 3d 

    02 81 80 
        20 ba 41 01 b4 c7 61 a0 69 75 7a cd e1 ac 42 9b dc 1b 27 31 09 96 4d 0f bf d0 6f 35 79 72 e4 b8 94 ff 12 6c 82 9a 4f fe 09 8f f4 d9 94 37 c4 0a 9a 9d dd 9b 13 3d 51 bd fa 0d 81 a9 65 1c 8f 29 b2 9f 8c a4 1f 4a b6 8b 5f 0a 24 af b6 88 9b 88 dc ec ec 34 34 10 60 4e c7 d2 11 aa 93 81 88 98 f5 ac 5c a9 a6 12 f4 8e 62 c9 f4 19 b7 d2 a5 fe 12 57 e9 a4 fd b3 63 3e 0d 07 dc 87 36 85 5a 44

再通过OpenSSL来查看一下密钥的各参数情况

Bash
openssl rsa -in 文件名 -text -noout
Bash
Private-Key: (2048 bit, 2 primes)
modulus:
    00:86:03:c1:d6:11:c8:af:b0:27:dc:70:c6:d5:da:
    89:a3:c5:10:bb:ee:68:38:b3:80:d7:5f:73:d2:b6:
    44:4a:8c:eb:af:5a:31:de:0d:0a:bb:ce:ad:d5:7d:
    4e:a5:b6:b0:5a:bf:1c:17:35:e0:62:c0:ed:81:93:
    d6:84:41:31:6e:c5:3f:a8:71:99:5d:ec:28:f2:87:
    de:ff:84:9a:fe:cd:43:3a:95:26:64:81:6c:33:f6:
    00:b5:61:1e:c7:e6:93:c0:0f:45:4f:ee:84:67:73:
    98:25:ab:66:63:d8:aa:f5:ec:27:02:37:82:74:1c:
    25:97:4e:02:f2:f4:fd:29:ee:f7:5e:77:67:07:cb:
    53:b4:bd:92:e2:12:1c:bb:3e:ef:55:dd:47:4d:94:
    9f:43:ea:2e:b0:b5:0c:e4:eb:93:7b:4d:fa:d4:d1:
    6a:a7:84:45:86:11:e3:b4:cf:5c:ba:fc:9a:3d:a3:
    05:bd:11:68:7a:65:f2:08:7f:29:7f:18:25:e2:59:
    45:48:12:62:b4:f0:1e:2c:1c:47:a5:5f:4f:8e:52:
    c1:a7:89:18:23:33:eb:60:33:29:d4:01:76:b1:e4:
    32:bd:bf:16:36:e8:c5:72:ed:19:bc:e6:51:b8:7c:
    54:2e:25:13:d9:12:36:b2:b6:7a:7b:20:28:ab:b4:
    57:8b
publicExponent: 65537 (0x10001)
privateExponent:
    4f:f2:c3:db:6a:66:97:f0:f6:06:bb:3c:d3:6d:79:
    39:3b:22:1d:8b:3f:9f:bb:ee:7c:0d:d4:b1:fa:cc:
    48:d1:42:f5:2c:50:9d:90:98:3b:ac:33:e6:2d:c8:
    12:72:27:49:2b:c3:9d:51:25:d8:a4:11:cc:80:9e:
    b3:ed:94:88:e9:a1:6d:62:bf:c4:d9:53:20:44:d0:
    53:0d:0f:ce:3c:34:04:29:e0:6a:8b:0f:3a:fc:62:
    35:d2:87:a3:67:b9:f3:14:e9:1b:a3:46:57:d5:0d:
    0a:43:f3:64:53:68:c2:27:2c:aa:54:e5:3d:51:61:
    57:9f:51:81:11:88:4a:50:08:98:f3:0d:dd:9f:6f:
    55:25:08:9c:7a:5d:98:2e:4d:74:ec:a8:29:a1:af:
    b2:eb:eb:13:75:5a:f4:9d:4d:8d:9a:ed:c3:f1:9e:
    eb:20:82:b0:9a:89:8c:68:0a:32:50:44:f9:7e:67:
    83:b7:de:01:e4:5c:d6:d9:9d:c8:79:62:51:10:ba:
    2d:7e:7d:a9:e8:7c:ab:a4:94:ad:e6:01:ba:81:c2:
    8f:e5:7d:75:c0:bb:91:9b:84:c7:48:67:a2:30:eb:
    cb:c0:88:52:50:b6:ec:2f:39:81:35:87:ab:0d:dc:
    41:ec:8e:77:e4:6d:f8:02:fa:eb:b8:d5:04:1f:02:
    f1
prime1:
    00:c8:11:62:77:3f:30:de:28:c7:5b:e5:c3:70:7d:
    b4:26:11:f1:10:c0:26:15:35:1f:d1:f5:c8:d7:5c:
    77:10:29:3e:e9:cd:b2:00:7d:6f:0c:73:38:ae:bd:
    ca:1e:ca:55:84:42:73:c8:d5:6c:54:18:35:23:82:
    45:e6:04:a4:3d:6b:32:82:37:68:c8:c3:ea:8d:0f:
    4c:d1:83:f6:42:1a:3a:8a:28:b3:db:47:37:c3:e2:
    bd:70:17:b3:4c:db:43:9f:e3:7f:82:a3:ab:24:b0:
    46:3a:9a:ee:d7:3c:29:7e:ba:12:e2:27:bb:e9:03:
    69:01:46:7c:dc:e4:14:56:b9
prime2:
    00:ab:7b:06:07:4a:d9:be:7d:fe:2d:b4:37:4b:f0:
    25:c9:da:67:65:87:fa:7b:ce:45:52:dd:df:42:e9:
    11:04:8e:2f:2e:5e:92:a4:e3:09:49:97:d0:41:49:
    cc:f8:fd:3d:b7:6c:fb:ac:7d:f0:e1:02:ce:61:b7:
    c4:9d:db:75:23:f3:0c:85:f5:9b:ed:af:7a:23:f0:
    9f:c3:d9:96:e3:c4:48:52:b9:38:31:4b:55:fc:fa:
    bb:6c:ac:f4:bf:3a:bf:0c:19:c0:e3:89:c0:b1:ae:
    3a:6d:bf:72:b2:32:7a:f9:f7:27:2d:63:4b:5b:3e:
    0e:f7:eb:c1:78:56:86:3e:63
exponent1:
    7e:be:74:b0:b2:b9:94:a4:24:d7:d5:11:21:3c:a1:
    59:d4:d7:f1:12:a9:8d:99:e0:14:b5:2e:8d:30:a8:
    c4:f6:3c:43:05:ea:94:60:e2:6e:f2:b7:a0:0b:df:
    f5:ed:9c:8b:73:45:c2:9f:62:47:4c:26:ad:de:23:
    c5:fa:7e:c9:e2:15:57:65:1e:de:c1:2e:e0:3a:27:
    22:3c:05:50:0f:29:b2:c8:e2:58:32:1f:e3:da:ca:
    c4:2e:5f:8a:7b:05:b6:4e:fb:42:99:c9:1c:2f:07:
    ee:ac:a4:4e:7a:70:97:35:ee:2c:93:9b:79:9c:8d:
    73:1f:62:8e:d5:77:05:11
exponent2:
    7f:43:78:3f:e5:3f:20:6e:e5:3b:0a:2a:97:c3:0c:
    68:78:1c:90:6f:90:68:5d:b7:79:3e:8c:b9:5d:bb:
    71:84:0c:63:ff:70:19:cb:1d:53:6e:c5:f2:0e:51:
    7e:e0:82:f3:24:80:cb:8d:54:3d:ca:15:5c:d4:43:
    a9:5b:fb:dd:c8:3d:9d:15:a0:b9:8f:a7:b8:10:96:
    a1:85:54:c4:89:21:9b:b3:36:8e:ee:f8:4c:1d:d1:
    07:60:7c:eb:87:2f:ce:4d:36:a8:27:cd:c8:90:23:
    70:db:ca:c5:91:4d:4f:42:2b:e3:35:73:f1:ad:66:
    f2:7c:17:43:b6:03:3f:3d
coefficient:
    20:ba:41:01:b4:c7:61:a0:69:75:7a:cd:e1:ac:42:
    9b:dc:1b:27:31:09:96:4d:0f:bf:d0:6f:35:79:72:
    e4:b8:94:ff:12:6c:82:9a:4f:fe:09:8f:f4:d9:94:
    37:c4:0a:9a:9d:dd:9b:13:3d:51:bd:fa:0d:81:a9:
    65:1c:8f:29:b2:9f:8c:a4:1f:4a:b6:8b:5f:0a:24:
    af:b6:88:9b:88:dc:ec:ec:34:34:10:60:4e:c7:d2:
    11:aa:93:81:88:98:f5:ac:5c:a9:a6:12:f4:8e:62:
    c9:f4:19:b7:d2:a5:fe:12:57:e9:a4:fd:b3:63:3e:
    0d:07:dc:87:36:85:5a:44

经过对比可以知道,结构为

Text Only
RSAPrivateKey ::= SEQUENCE {
 version Version,
 modulus INTEGER, -- n
 publicExponent INTEGER, -- e
 privateExponent INTEGER, -- d
 prime1 INTEGER, -- p
 prime2 INTEGER, -- q
 exponent1 INTEGER, -- d mod (p-1)
 exponent2 INTEGER, -- d mod (q-1)
 coefficient INTEGER -- (inverse of q) mod p 
}

2048bit的私钥文件和1024bit的私钥文件对比

2048 02820101(n) 0203(e) 028201(d) 028181(p) 028181(q) 028180(d % (p-1)) 028180(d % (q-1)) 028180(invert(q,p))
1024 028181(n) 0203(e) 028180(d) 024100(p) 024100(q) 0240 0240 0240

这个表也没权威性,找参数就找02这样的分隔符就好了。

NSSCTFRound16——break

题目给出如下文件:

pri-break.pem

Python
Bc8tSTrvGJm2oYuCzIz+Yg4nwwKBgQDiYUawe5Y+rPbFhVOMVB8ZByfMa4LjeSDd
Z23jEGvylBHSeyvFCQq3ISUE40k1D2XmmeaZML3a1nUn6ORIWGaG2phcwrWLkR6n
ubVmb1QJSzgzmFHGnL56KHByZxD9q6DPB+o6gGWt8/6ddBl2NIZU/1btdPQgojfA
XXJFzR92RQKBgQC7qlB0U7m2U4FdG9eelSd+WSKNUVllZAuHji7jgh7Ox6La9xN5
miGZ1yvP44yX218OJ9Zi08o6vIrM6Eil45KzTtGm4iuIn8CMpox+5eUtoxyvxa9r
s2Wu+IRZN9zCME+p+qI8/TG27dIyDzsdgNqcUo8ESls7uW5/FEA7bYTCiQKBgQC7
1KybeB+kZ0zlfIdi8tVOpeI+uaHDbdh3+/5wHUsD3hmfg7VAag0q/2RA1vkB/oG1
QVLVHl0Yu0I/1/u5jyeakrtClAegAsvlrK+3i321rGS4YpTPb3SX1P/f3GZ7o7Ds
touA+NHk8IL9T7xkmJYw5h/RLG32ucH6aU6MXfLR5QKBgD/skfdFxGWxhHk6U1mS
27IM9jJNg9xLz5nxzkqPPhLn+rdgIIuTuQtv++eEjEP++7ZV10rg5yKVJd/bxy8H
2IN7aQo7kZWulHTQDZMFwgOhn0u6glJi+qC8bWzYDFOQSFrY9XQ3vwKMspqm+697
xM+dMUW0LML6oUE9ZjEiAY/5
-----END PRIVATE KEY-----

cipher.txt

Text Only
6081370370545409218106271903400346695565292992689150366474451604281551878507114813906275593034729563149286993189430514737137534129570304832172520820901940874698337733991868650159489601159238582002010625666203730677577976307606665760650563172302688129824842780090723167480409842707790983962415315804311334507726664838464859751689906850572044873633896253285381878416855505301919877714965930289139921111644393144686543207867970807469735534838601255712764863973853116693691206791007433101433703535127367245739289103650669095061417223994665200039533840922696282929063608853551346533188464573323230476645532002621795338655

cipher中这个值是2046bit,可以推出n是2048bit的

这个私钥文件的内容转成16进制得到

Text Only
        05 cf 2d 49 3a ef 18 99 b6 a1 8b 82 cc 8c fe 62 0e 27 c3 

    02 81 81 00 
        e2 61 46 b0 7b 96 3e ac f6 c5 85 53 8c 54 1f 19 07 27 cc 6b 82 e3 79 20 dd 67 6d e3 10 6b f2 94 11 d2 7b 2b c5 09 0a b7 21 25 04 e3 49 35 0f 65 e6 99 e6 99 30 bd da d6 75 27 e8 e4 48 58 66 86 da 98 5c c2 b5 8b 91 1e a7 b9 b5 66 6f 54 09 4b 38 33 98 51 c6 9c be 7a 28 70 72 67 10 fd ab a0 cf 07 ea 3a 80 65 ad f3 fe 9d 74 19 76 34 86 54 ff 56 ed 74 f4 20 a2 37 c0 5d 72 45 cd 1f 76 45 

    02 81 81 00 
        bb aa 50 74 53 b9 b6 53 81 5d 1b d7 9e 95 27 7e 59 22 8d 51 59 65 64 0b 87 8e 2e e3 82 1e ce c7 a2 da f7 13 79 9a 21 99 d7 2b cf e3 8c 97 db 5f 0e 27 d6 62 d3 ca 3a bc 8a cc e8 48 a5 e3 92 b3 4e d1 a6 e2 2b 88 9f c0 8c a6 8c 7e e5 e5 2d a3 1c af c5 af 6b b3 65 ae f8 84 59 37 dc c2 30 4f a9 fa a2 3c fd 31 b6 ed d2 32 0f 3b 1d 80 da 9c 52 8f 04 4a 5b 3b b9 6e 7f 14 40 3b 6d 84 c2 89 

    02 81 81 00 
        bb d4 ac 9b 78 1f a4 67 4c e5 7c 87 62 f2 d5 4e a5 e2 3e b9 a1 c3 6d d8 77 fb fe 70 1d 4b 03 de 19 9f 83 b5 40 6a 0d 2a ff 64 40 d6 f9 01 fe 81 b5 41 52 d5 1e 5d 18 bb 42 3f d7 fb b9 8f 27 9a 92 bb 42 94 07 a0 02 cb e5 ac af b7 8b 7d b5 ac 64 b8 62 94 cf 6f 74 97 d4 ff df dc 66 7b a3 b0 ec b6 8b 80 f8 d1 e4 f0 82 fd 4f bc 64 98 96 30 e6 1f d1 2c 6d f6 b9 c1 fa 69 4e 8c 5d f2 d1 e5 

    02 81 80 
        3f ec 91 f7 45 c4 65 b1 84 79 3a 53 59 92 db b2 0c f6 32 4d 83 dc 4b cf 99 f1 ce 4a 8f 3e 12 e7 fa b7 60 20 8b 93 b9 0b 6f fb e7 84 8c 43 fe fb b6 55 d7 4a e0 e7 22 95 25 df db c7 2f 07 d8 83 7b 69 0a 3b 91 95 ae 94 74 d0 0d 93 05 c2 03 a1 9f 4b ba 82 52 62 fa a0 bc 6d 6c d8 0c 53 90 48 5a d8 f5 74 37 bf 02 8c b2 9a a6 fb af 7b c4 cf 9d 31 45 b4 2c c2 fa a1 41 3d 66 31 22 01 8f f9

我们已知\(q,d_p,d_q,q^{-1}\)

直接用\(m = c^{d_p} \mod p\)解即可

exp

Python
from Crypto.Util.number import *

q = 0xe26146b07b963eacf6c585538c541f190727cc6b82e37920dd676de3106bf29411d27b2bc5090ab7212504e349350f65e699e69930bddad67527e8e448586686da985cc2b58b911ea7b9b5666f54094b38339851c69cbe7a2870726710fdaba0cf07ea3a8065adf3fe9d741976348654ff56ed74f420a237c05d7245cd1f7645
dp = 0xbbaa507453b9b653815d1bd79e95277e59228d515965640b878e2ee3821ecec7a2daf713799a2199d72bcfe38c97db5f0e27d662d3ca3abc8acce848a5e392b34ed1a6e22b889fc08ca68c7ee5e52da31cafc5af6bb365aef8845937dcc2304fa9faa23cfd31b6edd2320f3b1d80da9c528f044a5b3bb96e7f14403b6d84c289
dq = 0xbbd4ac9b781fa4674ce57c8762f2d54ea5e23eb9a1c36dd877fbfe701d4b03de199f83b5406a0d2aff6440d6f901fe81b54152d51e5d18bb423fd7fbb98f279a92bb429407a002cbe5acafb78b7db5ac64b86294cf6f7497d4ffdfdc667ba3b0ecb68b80f8d1e4f082fd4fbc64989630e61fd12c6df6b9c1fa694e8c5df2d1e5
inv = 0x3fec91f745c465b184793a535992dbb20cf6324d83dc4bcf99f1ce4a8f3e12e7fab760208b93b90b6ffbe7848c43fefbb655d74ae0e7229525dfdbc72f07d8837b690a3b9195ae9474d00d9305c203a19f4bba825262faa0bc6d6cd80c5390485ad8f57437bf028cb29aa6fbaf7bc4cf9d3145b42cc2faa1413d663122018ff9
c = 6081370370545409218106271903400346695565292992689150366474451604281551878507114813906275593034729563149286993189430514737137534129570304832172520820901940874698337733991868650159489601159238582002010625666203730677577976307606665760650563172302688129824842780090723167480409842707790983962415315804311334507726664838464859751689906850572044873633896253285381878416855505301919877714965930289139921111644393144686543207867970807469735534838601255712764863973853116693691206791007433101433703535127367245739289103650669095061417223994665200039533840922696282929063608853551346533188464573323230476645532002621795338655

m = pow(c,dq,q)
print(long_to_bytes(m))
## flag{oi!_you_find___what_i_Wa1t_talK_y0n!!!}

2022蓝帽杯——corrupted_key

task.py

Python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from secret import flag

key = RSA.generate(1024)
open("flag.enc",'wb').write(PKCS1_OAEP.new(key.publickey()).encrypt(flag))
open('priv.pem','wb').write(key.exportKey('PEM'))

priv.pem

Text Only
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDXFSUGqpzsBeUzXWtG9UkUB8MZn9UQkfH2Aw03YrngP0nJ3NwH
UFTgzBSLl0tBhUvZO07haiqHbuYgBegO+Aa3qjtksb+bH6dz41PQzbn/l4Pd1fXm
dJmtEPNh6TjQC4KmpMQqBTXF52cheY6GtFzUuNA7DX51wr6HZqHoQ73GQQIDAQAB








yQvOzxy6szWFheigQdGxAkEA4wFss2CcHWQ8FnQ5w7k4uIH0I38khg07HLhaYm1c
zUcmlk4PgnDWxN+ev+vMU45O5eGntzaO3lHsaukX9461mA==
-----END RSA PRIVATE KEY-----

把已有的内容进行解码,可以得到\(n,e\),inverse(q,p),以及\(d_q\)的低120位

Text Only
30 82 02 5e 
    02 01 
        00 

    02 81 81 00 
        d7 15 25 06 aa 9c ec 05 e5 33 5d 6b 46 f5 49 14 07 c3 19 9f d5 10 91 f1 f6 03 0d 37 62 b9 e0 3f 49 c9 dc dc 07 50 54 e0 cc 14 8b 97 4b 41 85 4b d9 3b 4e e1 6a 2a 87 6e e6 20 05 e8 0e f8 06 b7 aa 3b 64 b1 bf 9b 1f a7 73 e3 53 d0 cd b9 ff 97 83 dd d5 f5 e6 74 99 ad 10 f3 61 e9 38 d0 0b 82 a6 a4 c4 2a 05 35 c5 e7 67 21 79 8e 86 b4 5c d4 b8 d0 3b 0d 7e 75 c2 be 87 66 a1 e8 43 bd c6 41 

    02 03 
        01 00 01
Text Only
c9 0b ce cf 1c ba b3 35 85 85 e8 a0 41 d1 b1 

    02 41 00 
        e3 01 6c b3 60 9c 1d 64 3c 16 74 39 c3 b9 38 b8 81 f4 23 7f 24 86 0d 3b 1c b8 5a 62 6d 5c cd 47 26 96 4e 0f 82 70 d6 c4 df 9e bf eb cc 53 8e 4e e5 e1 a7 b7 36 8e de 51 ec 6a e9 17 f7 8e b5 98
\[ \because d_q \equiv d \mod q-1 \]
\[ \therefore d_q = d + k(q-1) \]

两边同乘e

\[ ed_q = ed + ke(q-1) \]
\[ \because ed = k_1(p-1)(q-1) + 1 \]
\[ \therefore ed_q = k_2(q-1) + 1 \]

\[ ed_q + k_2 - 1 = k_2q \]
\[ \therefore ed_q + k_2 -1 \equiv k_2q \mod 2^{120} \]
\[ \because dq < q-1 \]
\[ \therefore e > k \]

枚举k,解方程即求出所有\(q\)的低120位的可能取值

\[ \because inv ×q \equiv 1 \mod p \]
\[ inv\times q -1 = kp \]

两边同乘q得到

\[ inv \times q\times q -q = kn \]

\[ inv \times q^2 - q \equiv 0 \mod n \]

然后用coper求解

exp.sage

Python
#sage
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from gmpy2 import *
from tqdm import *

n = 0xd7152506aa9cec05e5335d6b46f5491407c3199fd51091f1f6030d3762b9e03f49c9dcdc075054e0cc148b974b41854bd93b4ee16a2a876ee62005e80ef806b7aa3b64b1bf9b1fa773e353d0cdb9ff9783ddd5f5e67499ad10f361e938d00b82a6a4c42a0535c5e76721798e86b45cd4b8d03b0d7e75c2be8766a1e843bdc641
e = 0x10001
inv = 0xe3016cb3609c1d643c167439c3b938b881f4237f24860d3b1cb85a626d5ccd4726964e0f8270d6c4df9ebfebcc538e4ee5e1a7b7368ede51ec6ae917f78eb598
d_low = 0xc90bcecf1cbab3358585e8a041d1b1
q_low = []
c = open("flag.enc",'rb').read()

for i in trange(1,e):
    try:
        q0 = invert(i, 2 ** 120) * (e * d_low + i - 1) % 2 ^ 120
        q_low.append(q0)
    except:
        continue

PR.<x> = PolynomialRing(Zmod(n))

for i in trange(len(q_low)-1,-1,-1):
    f = inv * (2^120*x + int(q_low[i]))^2 - (2^120*x + int(q_low[i]))
    f = f.monic()
    root = f.small_roots(X = 2^(512-120))
    if root:
        q = 2^120 * int(root[0]) + int(q_low[i])
        p = n // q
        if p * q == n:
            d = gmpy2.invert(e, (p - 1) * (q - 1))
            print("p = ",p)
            print("q = ",q)
            print("d = ",d)
            key = RSA.construct((int(n),int(e),int(d),int(p),int(q)))
            newkey = PKCS1_OAEP.new(key)
            flag = newkey.decrypt(c)
            print(flag)
            break

2024CISCN——ezrsa

task.py

Python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import random
from secret import flag

m = bytes_to_long(flag)
key = RSA.generate(1024)
passphrase = str(random.randint(0,999999)).zfill(6).encode()
output = key.export_key(passphrase=passphrase).split(b'\n')
for i in range(7, 15):
    output[i] = b'*' * 64
with open("priv.pem", 'wb') as f:
    for line in output:
        f.write(line + b'\n')
with open("enc.txt", 'w') as f:
    f.write(str(key._encrypt(m)))

priv.pem

Text Only
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,435BF84C562FE793

9phAgeyjnJYZ6lgLYflgduBQjdX+V/Ph/fO8QB2ZubhBVOFJMHbwHbtgBaN3eGlh
WiEFEdQWoOFvpip0whr4r7aGOhavWhIfRjiqfQVcKZx4/f02W4pcWVYo9/p3otdD
ig+kofIR9Ky8o9vQk7H1eESNMdq3PPmvd7KTE98ZPqtIIrjbSsJ9XRL+gr5a91gH
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
hQds7ZdA9yv+yKUYv2e4de8RxX356wYq7r8paBHPXisOkGIVEBYNviMSIbgelkSI
jLQka+ZmC2YOgY/DgGJ82JmFG8mmYCcSooGL4ytVUY9dZa1khfhceg==
-----END RSA PRIVATE KEY-----

类似蓝帽杯2022的corrupted_key

不一样的是,这题得到的pem文件内容是加密的

根据代码

Python
key.export_key(passphrase=passphrase).split(b'\n')

找到Crypto下的源代码

RSA.py

发现关键代码

如果passphrase存在,则随机生成一个salt,然后使用PKBDF1方法加密两次拼接得到key。

此时使用的加密方式为3des,模式为cbc,iv为salt

由此我们可以根据passphrase的生成方式来爆破出唯一的passphrase

根据私钥的格式,开头一定是3082,再根据n为1024bit,那么在私钥中n的格式为028181,加密参数e大概率为65537,即0x10001

那么其在私钥中则为 0203010001。

解密后存在如下特征的明文,那么当前的passphrase为所求

Python
from Crypto.Cipher import DES3
from Crypto.Protocol.KDF import PBKDF1
from Crypto.Hash import MD5
import base64


enc1 = "9phAgeyjnJYZ6lgLYflgduBQjdX+V/Ph/fO8QB2ZubhBVOFJMHbwHbtgBaN3eGlhWiEFEdQWoOFvpip0whr4r7aGOhavWhIfRjiqfQVcKZx4/f02W4pcWVYo9/p3otdDig+kofIR9Ky8o9vQk7H1eESNMdq3PPmvd7KTE98ZPqtIIrjbSsJ9XRL+gr5a91gH"
enc2 = "hQds7ZdA9yv+yKUYv2e4de8RxX356wYq7r8paBHPXisOkGIVEBYNviMSIbgelkSIjLQka+ZmC2YOgY/DgGJ82JmFG8mmYCcSooGL4ytVUY9dZa1khfhceg=="

salt = bytes.fromhex("435bf84c562fe793")
dict = [str(i).zfill(6).encode() for i in range(0,1000000)]
for passphrase in dict:
    key = PBKDF1(passphrase, salt, 16, 1, MD5)
    key += PBKDF1(key + passphrase, salt, 8, 1, MD5)
    cipher = DES3.new(key, DES3.MODE_CBC, salt)
    data = cipher.decrypt(base64.b64decode(enc1)).hex()
    if data.startswith("3082") and "028181" in data and "0203010001" in data:
        print(data)
        print(passphrase)
        data2 = cipher.decrypt(base64.b64decode(enc2)).hex()
        print(data2)
        break

得到

Text Only
30 82 02 5c
    02 01
        00       

    02 81 81 00
a1 8f 01 1b eb ac ce da 1c 68 12 73 0b 9e 62 72 0d 3c bd 68 57 af 2c f8 43 18 60 f5 dc 83 c5 52 0f 24 2f 3b e7 c9 e9 6d 7f 96 b4 18 98 ff 00 0f db 7e 43 ef 6f 1e 71 7b 2b 79 00 f3 56 60 a2 1d 1b 16 b5 18 49 be 97 a0 b0 f7 cb cf 5c fe 0f 00 37 0c ce 61 93 fe fa 1f ed 97 b3 7b d3 67 a6 73 56 51 62 ce 17 b0 22 57 08 c0 32 96 1d 17 5b bc 2c 82 9b f2 e1 6e ab c7 e0 88 1f ec a0 97 5c 81

    02 03 
        01 00 01

6a 03 30 64 c5 a0 dff c8 f2 36 3b 34 0e 50 24 05 f1 52 c4 29 87 1a 7a cd d2 8b e1 b6 43 b4 65 28 00 b8 8a 3d 23 cc 57 47 7d 75 dd 55 55 b6 35 16 76 16 ef 5c 60 9d 69 ce 3c 2a ed cb 03 b6 2f 92 9b bc d8 91 ca dc 0b a0 31 ae 6f ec 8a 21 16 d0 80 80 80 80 80 80 80 8

再根据私钥格式提取n,e,dq,inv

因为本题中中间部分的私钥文件缺失,而且加密是cbc的,导致enc2第一块解密的结果错误,需要去掉,也就是说正确的dq只有48bit

因此得到

Python
n = 0xa18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 65537
dq_leak= 0x8f2363b340e5
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
\[ \because ed_q = 1 + k(q-1) \]
\[ \therefore kq = ed_q +k-1 \]

k可以爆破,但不知道q,所以没办法构造1元coppersmith

\[ \because inv \times q \equiv 1 \mod p \]
\[ \therefore inv \times q - 1 \equiv 0 \mod p \]

同乘kq

\[ inv \times q \times kq - kq \equiv 0 \mod n \]

此时会有个突兀的q,不好处理,我们再同乘k,得到

\[ inv \times (kq)^2 - k\times kq \equiv 0 \mod n \]

得到两个式子:

\[ tmp = e(x + dq_{low})+k-1 \]
\[ inv\times tmp^2 - k\times tmp \equiv 0 \mod n \]

恢复出dq之后,\(q = (ed_q + k - 1) // k\)

Python
from tqdm import *
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

n = 0xa18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 65537
dq_leak= 0x8f2363b340e5
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
c = 55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989

def coppersmith(k):
    R.<x> = PolynomialRing(Zmod(n))
    tmp = e * (x * 2^48 + dq_leak) + k - 1          ## kq
    f = inv * tmp^2 - k*tmp
    f = f.monic()
    x0 = f.small_roots(X=2^464,beta=1,epsilon=0.09)
    return x0

for k in trange(1e):
    x0 = coppersmith(k)
    if x0 != []:
        dq = int(x0[0]) * 2^48 + dq_leak
        q = (e*dq + k - 1) // k
        ## print(f"k = {k}")
        ## k = 47794
        p = n // q
        d = inverse(e,(p-1)*(q-1))
        m = pow(c,d,n)
        print(long_to_bytes(int(m)))
        ## b'flag{df4a4054-23eb-4ba4-be5e-15b247d7b819}'
        break

参考:20220709-蓝帽杯-CryptoSecWriteUp | 4XWi11's Blog

在求出q后,其实pow(c,dq,q)也能出flag,不用求p