- 题目
import sympy
from gmpy2 import gcd, invert
from random import randint
from Crypto.Util.number import getPrime, isPrime, getRandomNBitInteger, bytes_to_long, long_to_bytes
import base64
from zlib import *
flag = b"MRCTF{XXXX}"
base = 65537
def gen_prime(N):
A = 0
while 1:
A = getPrime(N)
if A % 8 == 5:
break
return A
def gen_p():
p = getPrime(1024)
q = getPrime(1024)
assert (p < q)
n = p * q
print("P_n = ", n)
F_n = (p - 1) * (q - 1)
print("P_F_n = ", F_n)
factor2 = 2021 * p + 2020 * q
if factor2 < 0:
factor2 = (-1) * factor2
return sympy.nextprime(factor2)
def gen_q():
p = getPrime(1024)
q = getPrime(1024)
assert (p < q)
n = p * q
print("Q_n = ", n)
e = getRandomNBitInteger(53)
F_n = (p - 1) * (q - 1)
while gcd(e, F_n) != 1:
e = getRandomNBitInteger(53)
d = invert(e, F_n)
print("Q_E_D = ", e * d)
factor2 = 2021 * p - 2020 * q
if factor2 < 0:
factor2 = (-1) * factor2
return sympy.nextprime(factor2)
if __name__ == "__main__":
_E = base
_P = gen_p()
_Q = gen_q()
assert (gcd(_E, (_P - 1) * (_Q - 1)) == 1)
_M = bytes_to_long(flag)
_C = pow(_M, _E, _P * _Q)
print("Ciphertext = ", _C)
'''
P_n = 14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024336556028267742021320891681762543660468484018686865891073110757394154024833552558863671537491089957038648328973790692356014778420333896705595252711514117478072828880198506187667924020260600124717243067420876363980538994101929437978668709128652587073901337310278665778299513763593234951137512120572797739181693
P_F_n = 14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024099427363967321110127562039879018616082926935567951378185280882426903064598376668106616694623540074057210432790309571018778281723710994930151635857933293394780142192586806292968028305922173313521186946635709194350912242693822450297748434301924950358561859804256788098033426537956252964976682327991427626735740
Q_n = 20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947
Q_E_D = 100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201
Ciphertext = 40855937355228438525361161524441274634175356845950884889338630813182607485910094677909779126550263304194796000904384775495000943424070396334435810126536165332565417336797036611773382728344687175253081047586602838685027428292621557914514629024324794275772522013126464926990620140406412999485728750385876868115091735425577555027394033416643032644774339644654011686716639760512353355719065795222201167219831780961308225780478482467294410828543488412258764446494815238766185728454416691898859462532083437213793104823759147317613637881419787581920745151430394526712790608442960106537539121880514269830696341737507717448946962021
'''
- 题目分析
题目分为三部分
定义 p 、 q p{、}q p、q求法,加密,打印提示
- 第一步,对 p p p进行求值
def gen_p():
p = getPrime(1024)
q = getPrime(1024)
assert (p < q)
n = p * q
print("P_n = ", n)
F_n = (p - 1) * (q - 1)
print("P_F_n = ", F_n)
factor2 = 2021 * p + 2020 * q
if factor2 < 0:
factor2 = (-1) * factor2
return sympy.nextprime(factor2)
用解方程组的方式求出 p p p的值,这里用到了sympy库的symbols、solve和next函数
求解也比较简单,需要注意的是正负号和解的大小,代码如下:
from sympy import symbols, solve, nextprime
from gmpy2 import mpz
'''
solve p script
'''
P_n = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024336556028267742021320891681762543660468484018686865891073110757394154024833552558863671537491089957038648328973790692356014778420333896705595252711514117478072828880198506187667924020260600124717243067420876363980538994101929437978668709128652587073901337310278665778299513763593234951137512120572797739181693)
P_F_n = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024099427363967321110127562039879018616082926935567951378185280882426903064598376668106616694623540074057210432790309571018778281723710994930151635857933293394780142192586806292968028305922173313521186946635709194350912242693822450297748434301924950358561859804256788098033426537956252964976682327991427626735740)
p, q = symbols("p, q")
exp_p = [p * q - P_n, (p - 1) * (q - 1) - P_F_n]
solve_p = solve(exp_p, [p, q])
if solve_p[0][0] < 0:
solve_p[0][0] = - solve_p[0][0]
if solve_p[0][1] < 0:
solve_p[0][1] = - solve_p[0][1]
assert solve_p[0][0] > 0 and solve_p[0][1] > 0
if solve_p[0][0] > solve_p[0][1]:
temp = solve_p[0][0]
solve_p[0][0] = solve_p[0][1]
solve_p[0][1] = temp
assert solve_p[0][0] < solve_p[0][1]
result_num = nextprime(int(2021 * solve_p[0][0] + 2020 * solve_p[0][1]))
print(f"p = {result_num}")
root@1ad9c6992813:~# python solve_p20230909.py
p = 479118055465195802861076643662451975441788371634793637145996217181496941123528933189555924873955626144432796811508165808623238468622989137235172879429170575533130989283494729924111842069786350352207565492494839924066840359984054733935671743878254997948119056311916492118488857415958325161799871082194782809717073
- 第二步,对 q q q进行求值
def gen_q():
p = getPrime(1024)
q = getPrime(1024)
assert (p < q)
n = p * q
print("Q_n = ", n)
e = getRandomNBitInteger(53)
F_n = (p - 1) * (q - 1)
while gcd(e, F_n) != 1:
e = getRandomNBitInteger(53)
d = invert(e, F_n)
print("Q_E_D = ", e * d)
factor2 = 2021 * p - 2020 * q
if factor2 < 0:
factor2 = (-1) * factor2
return sympy.nextprime(factor2)
这里要注意getRandomNBitInteger()函数
>>> from Crypto.Util.number import getRandomNBitInteger
>>> help(getRandomNBitInteger)
Help on function getRandomNBitInteger in module Crypto.Util.number:
getRandomNBitInteger(N, randfunc=None)
Return a random number with exactly N-bits,
i.e. a random number between 2**(N-1) and (2**N)-1.
If :data:`randfunc` is omitted, then :meth:`Random.get_random_bytes` is used.
.. deprecated:: 3.0
This function is for internal use only and may be renamed or removed in
the future.
>>>
表示e的取值范围在 2 ∗ ∗ 52 2 ** 52 2∗∗52 到 ( 2 ∗ ∗ 53 ) − 1 (2 ** 53) - 1 (2∗∗53)−1之间。
>>> 2 ** 52
4503599627370496
>>> 2 ** 53 -1
9007199254740991
>>>
没看到上面两个数字的时候,我尝试用爆破e的方式去解方程,结果在听到cpu风扇疯狂输出的时候,我知道我错了,马上来查看e的取值范围。
问题要回到数论知识来解决,首先,我们知道了 n n n的值和 e ∗ d e*d e∗d的值,
e ∗ d e*d e∗d意味着逆元 d d d可以求出来
e ∗ d ≡ 1 ( m o d φ ( n ) ) e ∗ d − 1 = k ∗ φ ( n ) k = e ∗ d − 1 φ ( n ) φ ( n ) = ( p − 1 ) ∗ ( q − 1 ) k = e ∗ d − 1 ( p − 1 ) ∗ ( q − 1 ) \begin{align} e*d&\equiv 1 \pmod {\varphi(n)}\\ e*d-1&=k*\varphi(n)\\ k &= \cfrac{e*d-1}{\varphi(n)}\\ \varphi(n)&=(p-1)*(q-1)\\ k&=\cfrac{e*d-1}{(p-1)*(q-1)}\\ \end{align} e∗de∗d−1kφ(n)k≡1(modφ(n))=k∗φ(n)=φ(n)e∗d−1=(p−1)∗(q−1)=(p−1)∗(q−1)e∗d−1
这里要建立一个已知数的不等式出来
∵ p ∗ q > ( p − 1 ) ∗ ( q − 1 ) ∴ e ∗ d − 1 p ∗ q < e ∗ d − 1 ( p − 1 ) ∗ ( q − 1 ) ( 5 ) 、 ( 7 ) 两式可得: e ∗ d − 1 p ∗ q < k \begin{align} \because& p*q>(p-1)*(q-1) \\ \therefore& \cfrac{e*d-1}{p*q}<\cfrac{e*d-1}{(p-1)*(q-1)}\\ &{(5)、(7)两式可得:}\\ &\cfrac{e*d-1}{p*q}<k \end{align} ∵∴p∗q>(p−1)∗(q−1)p∗qe∗d−1<(p−1)∗(q−1)e∗d−1(5)、(7)两式可得:p∗qe∗d−1<k
这里求出了一个 k k k的取值范围,目测可以爆破 k k k
k ∣ ( e ∗ d − 1 ) ( e ∗ d − 1 ) % k = = 0 \begin{align} k\mid{(e*d-1)}\\ {(e*d-1)}\ \%\ k\ ==\ 0 \end{align} k∣(e∗d−1)(e∗d−1) % k == 0
代码如下:
from sympy import symbols, solve, nextprime
from gmpy2 import mpz
'''
solve p script for
'''
Q_n = mpz(20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947)
Q_E_D = mpz(100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201)
p, q,= symbols("p, q")
k = (Q_E_D - 1) // Q_n
print(f"origin_k = {k}")
for i in range(1000):
temp = k
if (Q_E_D - 1) % temp == 0:
print(f"k = {k}")
break
else:
k += 1
F_n = (Q_E_D - 1) // k
exp_q = [(p - 1) * (q - 1) - F_n, p * q - Q_n]
solve_q = sovle(exp_q, [p, q])
if solve_q[0][0] < 0:
solve_q[0][0] = - solve_q[0][0]
if solve_q[0][1] < 0:
solve_q[0][1] = - solve_q[0][1]
assert solve_q[0][0] > 0 and solve_q[0][1] > 0
if solve_p[0][0] > solve_p[0][1]:
temp = solve_p[0][0]
solve_p[0][0] = solve_p[0][1]
solve_p[0][1] = temp
assert solve_p[0][0] < solve_p[0][1]
test_num = int(2021 * solve_p[0][0] - 2020 * solve_p[0][1])
if test_num < 0:
test_num = - test_num
assert test_num > 0
result_num = nextprime(test_num)
print(f"q = {result_num}")
- 这里可以看出所求的 k k k值比我们所爆破的 k k k取值范围 ( o r i g i n _ k < k ) (origin\_k<k) (origin_k<k)第一个值,也许只需要第一个值就可以吗,或许型改变后有别的值,暂时无法证明。
root@1ad9c6992813:~# python solve_q20230909.py
origin_k = 4864856032156929
k = 4864856032156930
q = 103522908254080567893731908833394744488888808537035795737806298690667500329637779495333978590640235970136224577411196764442973344806294490943711807196503524248389571078345895360591904238447631800263183886834890874869994569137132568744715109230815151947999678985103201991854037190640567921343350745689008509490943
- 答案
root@1ad9c6992813:~# python
Python 3.10.1 (main, Dec 21 2021, 09:01:08) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from gmpy2 import mpz, invert, powmod
>>> p = mpz(479118055465195802861076643662451975441788371634793637145996217181496941123528933189555924873955626144432796811508165808623238468622989137235172879429170575533130989283494729924111842069786350352207565492494839924066840359984054733935671743878254997948119056311916492118488857415958325161799871082194782809717073)
>>> q = mpz(103522908254080567893731908833394744488888808537035795737806298690667500329637779495333978590640235970136224577411196764442973344806294490943711807196503524248389571078345895360591904238447631800263183886834890874869994569137132568744715109230815151947999678985103201991854037190640567921343350745689008509490943)
>>> n = p * q
>>> phi_n = (p - 1) * (q - 1)
>>> e = mpz(65537)
>>> d = invert(e, phi_n)
>>> d
mpz(25619104604708417410320050530458034881600150797111063130768379814346454150671629646972653124949838220770761566801654921374186802824984507668463275780289290158483725589244523686524717462293228565604264232483295037449621276240030334223746568888207906585502690130520837378424044937400893594155562181585253037324650899305810951037961882947649166156170916206109282830877172012022926663978309814289645192403667255021702101606086348424070667948870606925663243489137252361613551633679779244536361140592813592882525095001572792576499174631148250245656269881147598790445875148296374657296186149866846454279941083071765309086328923425)
>>> c = mpz(40855937355228438525361161524441274634175356845950884889338630813182607485910094677909779126550263304194796000904384775495000943424070396334435810126536165332565417336797036611773382728344687175253081047586602838685027428292621557914514629024324794275772522013126464926990620140406412999485728750385876868115091735425577555027394033416643032644774339644654011686716639760512353355719065795222201167219831780961308225780478482467294410828543488412258764446494815238766185728454416691898859462532083437213793104823759147317613637881419787581920745151430394526712790608442960106537539121880514269830696341737507717448946962021)
>>> m = powmod(c, d, n)
>>> m
mpz(2084579198913972005798444900653791375947108611599538184307769554792061)
>>> flag = bytes.fromhex(hex(int(m))[2:])
>>> flag
b'MRCTF{Ju3t_@_31mp13_que3t10n}'
- 脚本
from sympy import symbols, solve, nextprime
from gmpy2 import mpz, invert, powmod, isprime
def set_cmp(a, b):
if a < 0:
a = - a
if b < 0:
b = - b
assert a > 0 and b > 0
if a > b:
temp = a
a = b
b = temp
assert a < b
def solve_P(P_n, P_F_n):
p, q = symbols("p, q")
exp_p = [p * q - P_n, (p - 1) * (q - 1) - P_F_n]
solve_p = solve(exp_p, [p, q])
set_cmp(solve_p[0][0], solve_p[0][1])
result_num = nextprime(int(2021 * solve_p[0][0] + 2020 * solve_p[0][1]))
return result_num
def solve_Q(Q_n, Q_E_D):
p, q,= symbols("p, q")
k = (Q_E_D - 1) // Q_n
for i in range(1000):
temp = k
if (Q_E_D - 1) % temp == 0:
print(f"k = {k}")
break
else:
k += 1
F_n = (Q_E_D - 1) // k
exp_q = [(p - 1) * (q - 1) - F_n, p * q - Q_n]
solve_q = solve(exp_q, [p, q])
set_cmp(solve_q[0][0], solve_q[0][1])
test_num = int(2021 * solve_q[0][0] - 2020 * solve_q[0][1])
if test_num < 0:
test_num = - test_num
assert test_num > 0
result_num = nextprime(test_num)
return result_num
def decode_rsa(c, e, p, q):
n = p * q
phi_n = (p - 1) * (q - 1)
d = invert(e, phi_n)
flag = bytes.fromhex(hex(int(powmod(c, d, n)))[2:])
return flag
P_n = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024336556028267742021320891681762543660468484018686865891073110757394154024833552558863671537491089957038648328973790692356014778420333896705595252711514117478072828880198506187667924020260600124717243067420876363980538994101929437978668709128652587073901337310278665778299513763593234951137512120572797739181693)
P_F_n = mpz(14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024099427363967321110127562039879018616082926935567951378185280882426903064598376668106616694623540074057210432790309571018778281723710994930151635857933293394780142192586806292968028305922173313521186946635709194350912242693822450297748434301924950358561859804256788098033426537956252964976682327991427626735740)
Q_n = mpz(20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947)
Q_E_D = mpz(100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201)
Ciphertext = mpz(40855937355228438525361161524441274634175356845950884889338630813182607485910094677909779126550263304194796000904384775495000943424070396334435810126536165332565417336797036611773382728344687175253081047586602838685027428292621557914514629024324794275772522013126464926990620140406412999485728750385876868115091735425577555027394033416643032644774339644654011686716639760512353355719065795222201167219831780961308225780478482467294410828543488412258764446494815238766185728454416691898859462532083437213793104823759147317613637881419787581920745151430394526712790608442960106537539121880514269830696341737507717448946962021)
p = solve_P(P_n, P_F_n)
q = solve_Q(Q_n, Q_E_D)
c = Ciphertext
e = mpz(65537)
assert isprime(p) and isprime(q)
flag = decode_rsa(c, e, p, q)
print(flag)