[MRCTF2020]Easy_RSA
先来分析一下这个RSA代码的特殊性,这个不是传统的RSA,随机生成N,并保证为N%8的余数是5
zlib 用于数据压缩,但是并似乎没有用到
gen_p():
- 生成随机的1024位质数p。
- 计算n=p*q,并没有直接用于加密。
- 计算F_n=(p-1)*(q-1) ,用于计算欧拉值
- 计算factor,若是负数,则取其绝对值。
- 使用**
sympy.nextprime(factor2)
** 找到大于factor2的最小质数,并返回这个质数为_P
gen_q():
- 生成随机的1024位质数q。
- 生成一个随机的53位整数
e
作为公钥的指数候选。 - 检查e和F_N是否互质(即它们的最大公约数是否为1)。
- 使用扩展欧几里得算法计算d,使得e*d %F_N==1(这是RSA中的私钥指数).
那么基本思路就是:gen_p()中已知n与φ(n),直接联立解一元二次方程
gen_q()中已知e*d和n,分解n得p,q对应的P,Q求出来后,进行普通的RSA解密即可
现在就先解决p
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}")
解释:
sybols(用于定义符号变量)、solve(用于解方程)和 nextprime(用于找到大于给定数的最小质数)。
利用:p * q - P_n = 0
和 (p - 1) * (q - 1) - P_F_n = 0
。来求p
根据e*d mod φ(n) =1 来求φ(n)
首先:
φ(n)=(p-1)*(q-1)
e*d = kφ(n) +1=k(pq -p -q +1) +1 ==> k = (e * d -1)/(pq - p - q +1)
k值无法确定,上式难以求解。
p,q均为大质数,并且1<< p < q, 那么:
(pq - p - q +1) ≈( p-2)q≈ pq
k = (e * d -1)/(pq - p - q +1) ≈ (Q_E_D -1)/Q_n
由于分母放大,所求k值略小: k = ((Q_E_D-1)//Q_n)+1
from sympy import symbols, solve
from gmpy2 import mpz
P_n = mpz(...) # 替换为你给出的P_n的值
P_F_n = mpz(...) # 替换为你给出的P_F_n的值
p, q = symbols("p q")
exp_p = [p * q - P_n, (p - 1) * (q - 1) - P_F_n]
# solve函数返回的是一个列表,其中每个元素都是一个元组,包含p和q的解
solutions = solve(exp_p, [p, q], dict=True)
# 假设解是唯一的,并且我们只关心第一个解(如果有多个解)
solution = solutions[0]
# 确保p和q都是正数
p_value = abs(solution[p])
q_value = abs(solution[q])
# 确保p < q
if p_value > q_value:
p_value, q_value = q_value, p_value
# 现在p_value和q_value分别包含p和q的值
print(f"p = {p_value}")
print(f"q = {q_value}")
这个的话同时求出了p和q
按照常规流程最终得到答案
import sympy
import gmpy2
from Crypto.Util.number import getPrime, isPrime, getRandomNBitInteger, bytes_to_long, long_to_bytes
import base64
P_n = 14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024336556028267742021320891681762543660468484018686865891073110757394154024833552558863671537491089957038648328973790692356014778420333896705595252711514117478072828880198506187667924020260600124717243067420876363980538994101929437978668709128652587073901337310278665778299513763593234951137512120572797739181693
P_F_n = 14057332139537395701238463644827948204030576528558543283405966933509944444681257521108769303999679955371474546213196051386802936343092965202519504111238572269823072199039812208100301939365080328518578704076769147484922508482686658959347725753762078590928561862163337382463252361958145933210306431342748775024099427363967321110127562039879018616082926935567951378185280882426903064598376668106616694623540074057210432790309571018778281723710994930151635857933293394780142192586806292968028305922173313521186946635709194350912242693822450297748434301924950358561859804256788098033426537956252964976682327991427626735740
Q_n = 20714298338160449749545360743688018842877274054540852096459485283936802341271363766157976112525034004319938054034934880860956966585051684483662535780621673316774842614701726445870630109196016676725183412879870463432277629916669130494040403733295593655306104176367902352484367520262917943100467697540593925707162162616635533550262718808746254599456286578409187895171015796991910123804529825519519278388910483133813330902530160448972926096083990208243274548561238253002789474920730760001104048093295680593033327818821255300893423412192265814418546134015557579236219461780344469127987669565138930308525189944897421753947
Q_E_D = 100772079222298134586116156850742817855408127716962891929259868746672572602333918958075582671752493618259518286336122772703330183037221105058298653490794337885098499073583821832532798309513538383175233429533467348390389323225198805294950484802068148590902907221150968539067980432831310376368202773212266320112670699737501054831646286585142281419237572222713975646843555024731855688573834108711874406149540078253774349708158063055754932812675786123700768288048445326199880983717504538825498103789304873682191053050366806825802602658674268440844577955499368404019114913934477160428428662847012289516655310680119638600315228284298935201
Ciphertext = 40855937355228438525361161524441274634175356845950884889338630813182607485910094677909779126550263304194796000904384775495000943424070396334435810126536165332565417336797036611773382728344687175253081047586602838685027428292621557914514629024324794275772522013126464926990620140406412999485728750385876868115091735425577555027394033416643032644774339644654011686716639760512353355719065795222201167219831780961308225780478482467294410828543488412258764446494815238766185728454416691898859462532083437213793104823759147317613637881419787581920745151430394526712790608442960106537539121880514269830696341737507717448946962021
base = 65537
p_q = P_n - P_F_n + 1
p1 = (p_q - gmpy2.iroot(p_q * p_q - 4 * P_n, 2)[0]) // 2
q1 = P_n // p1
factor1 = 2021 * p1 + 2020 * q1
if factor1 < 0:
factor1 = (-1) * factor1
p = sympy.nextprime(factor1)
p2 = 120538849514661970159855851547577637711900368732462953774738483480759950867244867240401273864984981385806453735655967797329769252143125966966236767391995563418243748302685348336642872306042286401427581501609713577329945760930395130411743322595026287853073310150103535873078436896035943385067893062698858976291
q2 = 171847486694659608706336923173786708071603689972942289760669690002615525263534483261477699540482615520223300780778172120221008417518590133753701145591943840552802072474293556608389677806415392384924913911677288126066245025731416399656855625839288752326267741979436855441260177305707529456715625062080892327017
factor2 = 2021 * p2 - 2020 * q2
if factor2 < 0:
factor2 = (-1) * factor2
q = sympy.nextprime(factor2)
print(q)
n = p * q
phi = (p - 1) * (q - 1)
e = 65537
d = gmpy2.invert(e, phi)
m = gmpy2.powmod(Ciphertext, d, n)
print(long_to_bytes(m))
再来梳理一下,本题的特点,就是列方程求p和q,要利用P_n和P_F_n是给定的。使用了solve函数来找到这两个变量的解。然后按常规办法就可以解决了。
2024黄河流域竞赛easyRSA
题目
from Crypto.Util.number import *
from random import shuffle
flag = b''
p=getPrime(1024)
q=getPrime(1024)
n=p*q
e=65537
m=bytes_to_long(flag)
c=pow(m,e,p*q)
p_bin=bin(p)[2:]
q_bin=bin(q)[2:]
x=[]
y=[]
for i in range(0,1024,256):
x.append(p_bin[i:i+256])
y.append(q_bin[i:i+256])
z=x+y
shuffle(z)
print(z)
print(c)
'''
['0111101011111101000011111100011110110111100011001110111101110110110001110111000100101100110101001010101100011100110110011011111110110111010000000011010010100000110101110011101000100100100011101101100010100001010100000000010000100101111100111111111101111010', '1101000000101000011011010111111110011110001001101011001011000101110000010100100011011101011101011110001010111111111111011110000110111011010011000110111010010110000101001000000101010011000100111000111001001100011010100011100000000010100011100000111110001110', '1010011111001110000111011001100100011011011000101010010000001011010011011111010001000101110101010101011101011110101101011010110101010000010100010111111111101110010110011001111010000100100011011100000111110001110000010001010101101101101110000011011000101011', '0100110010000010001110101000101000111011111110000001000001100010000000111111101100100111110011000100111010101000100011001110101100001011110010111000010001001010010000000011111000101011111010111101000001000000001101100100101001100010000100001111100111111010', '0111001101001010111111101010011101100000011001101111011010001000110010111010111111101000000001001011000001000110100111110001000011011000011110110101100100101011101010110001110110010000100001101110000000100011011101110111011111011000001100001000000110110011', '0110111010001001010000000001000001100110100100010010011100110011101111000101110100000000000100010111111110001010111111010111011011001001010110110000110001010011000111010110100100010100100111100101001010101011011110001101110101100010110010111100100101010111', '1011101011100010101111010010000100010011111101101011011110000010010000011101010001001010000000111100000100001011001111100100000100101001111010010110000001010101111110111001011111000110110110100101111000010000100010010111010000100110010100011000000100001111', '1001101111111001100011101111011101101011100110100011000100111100010101011001100001011000110110000000101011010100110001110000110001110111111001010010101000100110011010111101101101001101011111000111010000110001110000000001000111000001011110010000001111010011']
13003314666259100924056004488192879951991824059911508262140097706018888705955470264051361935101455340094971529836127008746457786892275972565449113840328391209339169000395470738780766025271862970759571146381234096267258200970822986151050047882589038375903133053657805275788419550498651112234266443422536799276888423014856283593968916394415760552426237539489292338903806512371485857542164997008922471994591413796274552848851859976522014922044409925823881793117165653534217369362159387632321283129023449326780775659395803089477090226867368824000190350663854144716638637456141115609096321561091304903356851544525526615903
'''
简单来分析
p****和q
的二进制拆分和混洗:
将p和q的二进制表示拆分成256位的块,并将它们混洗。
shuffle
的使用:
混洗操作不会改变p
和q
的数值,只是改变了它们二进制表示块的顺序。
同样的话,p和q的值不知道,理想条件下使用爆破的方法,找到正确的p和q
这个就是利用爆破的方法(因为分为8个模块,每个模块256位,如果太多的话穷举爆破的方法不合适)来解决p和q,并进行解密得到flag
from Crypto.Util.number import long_to_bytes, inverse, GCD, getPrime
import itertools
from sympy import isprime
# 混洗后二进制块的列表
shuffled_blocks = [
'0111101011111101000011111100011110110111100011001110111101110110110001110111000100101100110101001010101100011100110110011011111110110111010000000011010010100000110101110011101000100100100011101101100010100001010100000000010000100101111100111111111101111010',
'1101000000101000011011010111111110011110001001101011001011000101110000010100100011011101011101011110001010111111111111011110000110111011010011000110111010010110000101001000000101010011000100111000111001001100011010100011100000000010100011100000111110001110',
'1010011111001110000111011001100100011011011000101010010000001011010011011111010001000101110101010101011101011110101101011010110101010000010100010111111111101110010110011001111010000100100011011100000111110001110000010001010101101101101110000011011000101011',
'0100110010000010001110101000101000111011111110000001000001100010000000111111101100100111110011000100111010101000100011001110101100001011110010111000010001001010010000000011111000101011111010111101000001000000001101100100101001100010000100001111100111111010',
'0111001101001010111111101010011101100000011001101111011010001000110010111010111111101000000001001011000001000110100111110001000011011000011110110101100100101011101010110001110110010000100001101110000000100011011101110111011111011000001100001000000110110011',
'0110111010001001010000000001000001100110100100010010011100110011101111000101110100000000000100010111111110001010111111010111011011001001010110110000110001010011000111010110100100010100100111100101001010101011011110001101110101100010110010111100100101010111',
'1011101011100010101111010010000100010011111101101011011110000010010000011101010001001010000000111100000100001011001111100100000100101001111010010110000001010101111110111001011111000110110110100101111000010000100010010111010000100110010100011000000100001111',
'1001101111111001100011101111011101101011100110100011000100111100010101011001100001011000110110000000101011010100110001110000110001110111111001010010101000100110011010111101101101001101011111000111010000110001110000000001000111000001011110010000001111010011'
]
c = 13003314666259100924056004488192879951991824059911508262140097706018888705955470264051361935101455340094971529836127008746457786892275972565449113840328391209339169000395470738780766025271862970759571146381234096267258200970822986151050047882589038375903133053657805275788419550498651112234266443422536799276888423014856283593968916394415760552426237539489292338903806512371485857542164997008922471994591413796274552848851859976522014922044409925823881793117165653534217369362159387632321283129023449326780775659395803089477090226867368824000190350663854144716638637456141115609096321561091304903356851544525526615903
e = 65537
# 尝试所有排列来找到p和q
for perm in itertools.permutations(shuffled_blocks):
p_guess = int(''.join(perm[:4]), 2)
q_guess = int(''.join(perm[4:]), 2)
if isprime(p_guess) and isprime(q_guess):
n_guess = p_guess * q_guess
if c < n_guess:
phi_n = (p_guess - 1) * (q_guess - 1)
d = inverse(e,phi_n)
message = pow(c, d, n_guess)
print(long_to_bytes(message))
break
解释:遍历shuffled_blocks预期是一个包含二进制位块的列表)的所有排列来求p和q
使用itertools.permutations遍历shuffled_blocks的所有排列。
具体过程:
取排列的前四个元素,将它们连接成一个字符串,并转换为整数p_guess(假设这四个元素代表p的二进制表示)。
取排列的接下来(即第5个到最后的)元素,同样将它们连接成一个字符串,并转换为整数q_guess(假设这些元素代表q的二进制表示)。
使用isprime函数检查p_guess和q_guess是否都是质数。如果p_guess和q_guess都是质数,则计算它们的乘积n_guess作为模数n的猜测。
检查c是否小于n_guess.在RSA中,密文c必须小于模数n,否则解密过程将不会得到正确的明文。
如果上述条件都满足,计算欧拉函数phi_n,然后使用inverse函数计算私钥d(即e关于phi_n的模逆元).后面就
找到了有效的p
和q
,就跳出循环。
而这个是大神的wp:
from itertools import product
from Crypto.Util.number import long_to_bytes,inverse
c=13003314666259100924056004488192879951991824059911508262140097706018888705955470264051361935101455340094971529836127008746457786892275972565449113840328391209339169000395470738780766025271862970759571146381234096267258200970822986151050047882589038375903133053657805275788419550498651112234266443422536799276888423014856283593968916394415760552426237539489292338903806512371485857542164997008922471994591413796274552848851859976522014922044409925823881793117165653534217369362159387632321283129023449326780775659395803089477090226867368824000190350663854144716638637456141115609096321561091304903356851544525526615903
table =[0,1,2,3,4,5,6,7]
F4= [
'0111101011111101000011111100011110110111100011001110111101110110110001110111000100101100110101001010101100011100110110011011111110110111010000000011010010100000110101110011101000100100100011101101100010100001010100000000010000100101111100111111111101111010',
'1101000000101000011011010111111110011110001001101011001011000101110000010100100011011101011101011110001010111111111111011110000110111011010011000110111010010110000101001000000101010011000100111000111001001100011010100011100000000010100011100000111110001110',
'1010011111001110000111011001100100011011011000101010010000001011010011011111010001000101110101010101011101011110101101011010110101010000010100010111111111101110010110011001111010000100100011011100000111110001110000010001010101101101101110000011011000101011',
'0100110010000010001110101000101000111011111110000001000001100010000000111111101100100111110011000100111010101000100011001110101100001011110010111000010001001010010000000011111000101011111010111101000001000000001101100100101001100010000100001111100111111010',
'0111001101001010111111101010011101100000011001101111011010001000110010111010111111101000000001001011000001000110100111110001000011011000011110110101100100101011101010110001110110010000100001101110000000100011011101110111011111011000001100001000000110110011',
'0110111010001001010000000001000001100110100100010010011100110011101111000101110100000000000100010111111110001010111111010111011011001001010110110000110001010011000111010110100100010100100111100101001010101011011110001101110101100010110010111100100101010111',
'1011101011100010101111010010000100010011111101101011011110000010010000011101010001001010000000111100000100001011001111100100000100101001111010010110000001010101111110111001011111000110110110100101111000010000100010010111010000100110010100011000000100001111',
'1001101111111001100011101111011101101011100110100011000100111100010101011001100001011000110110000000101011010100110001110000110001110111111001010010101000100110011010111101101101001101011111000111010000110001110000000001000111000001011110010000001111010011'
]
for i in product(table,repeat=4):
kk=F4[i[0]]+F4[i[1]]+F4[i[2]]+F4[i[3]]
p=int(kk,2)
d=inverse(65537,p-1)
j=pow(c,d,p)
flag=long_to_bytes(j)
if b"flag" in flag:
print(flag)
break
简单分析一下:
循环解密尝试 :
itertools.product
,用于笛卡尔积的迭代器,生成了table中所有可能的长度为4的组合
table=4 因为二进制混洗的p和q分为8个模块,一般来说就是每个都为4个模块
repeat=4 表示我们想要从table中取出4个元素的所有可能组合
kk=F4[i[0]]+F4[i[1]]+F4[i[2]]+F4[i[3]]
对于上述迭代器中的每个元组i,这行代码会取元组的第一个元素i[0],并使用它作为索引从F4
列表中获取一个值。然后,它会取元组的第二个元素i[1](即1),并从F4
中获取该索引处的值.类似地,它会取元组的第三和第四个元素,并从F4
中获取相应的值. 将这四个从F4
中获取的值相加,并将结果存储在kk
中
大神的wp就是比较好,严整
[NCTF2019]childRSA
题目:
给了n,那就分解一下,
结果分解出来了
知道了n,e,c,p,q,一切就简单了
但是这一题还可以用费马小定理来解决
from Crypto.Util.number import isPrime, sieve_base as primes, long_to_bytes
import gmpy2
e = 65537
n = 32849718197337581823002243717057659218502519004386996660885100592872201948834155543125924395614928962750579667346279456710633774501407292473006312537723894221717638059058796679686953564471994009285384798450493756900459225040360430847240975678450171551048783818642467506711424027848778367427338647282428667393241157151675410661015044633282064056800913282016363415202171926089293431012379261585078566301060173689328363696699811123592090204578098276704877408688525618732848817623879899628629300385790344366046641825507767709276622692835393219811283244303899850483748651722336996164724553364097066493953127153066970594638491950199605713033004684970381605908909693802373826516622872100822213645899846325022476318425889580091613323747640467299866189070780620292627043349618839126919699862580579994887507733838561768581933029077488033326056066378869170169389819542928899483936705521710423905128732013121538495096959944889076705471928490092476616709838980562233255542325528398956185421193665359897664110835645928646616337700617883946369110702443135980068553511927115723157704586595844927607636003501038871748639417378062348085980873502535098755568810971926925447913858894180171498580131088992227637341857123607600275137768132347158657063692388249513
c = 26308018356739853895382240109968894175166731283702927002165268998773708335216338997058314157717147131083296551313334042509806229853341488461087009955203854253313827608275460592785607739091992591431080342664081962030557042784864074533380701014585315663218783130162376176094773010478159362434331787279303302718098735574605469803801873109982473258207444342330633191849040553550708886593340770753064322410889048135425025715982196600650740987076486540674090923181664281515197679745907830107684777248532278645343716263686014941081417914622724906314960249945105011301731247324601620886782967217339340393853616450077105125391982689986178342417223392217085276465471102737594719932347242482670320801063191869471318313514407997326350065187904154229557706351355052446027159972546737213451422978211055778164578782156428466626894026103053360431281644645515155471301826844754338802352846095293421718249819728205538534652212984831283642472071669494851823123552827380737798609829706225744376667082534026874483482483127491533474306552210039386256062116345785870668331513725792053302188276682550672663353937781055621860101624242216671635824311412793495965628876036344731733142759495348248970313655381407241457118743532311394697763283681852908564387282605279108
num = 1
for i in primes:
num *= i
p = gmpy2.gcd(gmpy2.powmod(2, num, n) - 1, n)
q = n // p
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = gmpy2.powmod(c, d, n)
print(long_to_bytes(m))
那么就来详细讲一下费马小定理,
对于任意质数p和整数a,当a不是p的倍数时,有a^(p-1) ≡ 1 (mod p))
主要应用于:
- 密钥生成:在RSA加密算法中,首先选择两个不同的质数p和q,并计算n = p * q。选取一个整数e作为公钥,要求e与(p-1) * (q-1)互质(即gcd(e, (p-1) * (q-1)) = 1)。利用费马定理,可以找到另一个整数d,使得e * d ≡ 1 (mod (p-1) * (q-1))。这个d就是私钥。
- 加密过程:使用公钥e对明文m进行加密,得到密文c,具体操作为c = m^e mod n。
- 解密过程:利用私钥d对密文c进行解密,恢复出明文m,具体操作为m = c^d mod n。解密过程的正确性依赖于费马定理。具体来说,因为me ≡ m(e*d) (mod n)(根据费马定理的原理),所以me ≡ m (mod n),从而证明了m = cd mod n是正确的