题目:
from Crypto.Util.number import *
import sympy
from flag import flag
a=getPrime(512)
p=sympy.nextprime(13*a)
q=sympy.prevprime(25*a)
number2=p*q
def crypto01(number1, number2, number3):
number4 = 1
while number2 > 0:
if number2 % 2:
number4 = (number4 * number1) % number3
number1 = number1 ** 2 % number3
number2 //= 2
return number4
def crypto02(number1, number2):
number3 = number1
number4 = number2
giao = 1
giaogiao = 0
while number4 > 0:
number7 = number3 // number4
giao, giaogiao = giaogiao, giao - giaogiao*number7
number3, number4 = number4, number3 - number4*number7
while giao<0:
giao = giao + number2
return giao
def crypto03(number1, number2, number3):
number4 = crypto01(number3, number1, number2)
return number4
def crypto05(number1,number2):
return pow(number1,0xe18e,number2)
number1 = 6035830951309638186877554194461701691293718312181839424149825035972373443231514869488117139554688905904333169357086297500189578624512573983935412622898726797379658795547168254487169419193859102095920229216279737921183786260128443133977458414094572688077140538467216150378641116223616640713960883880973572260683
number2 = 20163906788220322201451577848491140709934459544530540491496316478863216041602438391240885798072944983762763612154204258364582429930908603435291338810293235475910630277814171079127000082991765275778402968190793371421104016122994314171387648385459262396767639666659583363742368765758097301899441819527512879933947
number3 = int.from_bytes(flag[0:19].encode("utf-8"), "big")
number4 = int.from_bytes(flag[19:39].encode("utf-8"), "big")
print(crypto03(number1, number2, number3))
print(crypto05(number4,number2))
#6624758244437183700228793390575387439910775985543869953485120951825790403986028668723069396276896827302706342862776605008038149721097476152863529945095435498809442643082504012461883786296234960634593997098236558840899107452647003306820097771301898479134315680273315445282673421302058215601162967617943836306076
#204384474875628990804496315735508023717499220909413449050868658084284187670628949761107184746708810539920536825856744947995442111688188562682921193868294477052992835394998910706435735040133361347697720913541458302074252626700854595868437809272878960638744881154520946183933043843588964174947340240510756356766
1. 题目分析
题目给出了以下参数:
-
number1
:可能是第一个 RSA 的加密指数e1
或解密指数d1
。 -
number2
:模数n
(由p
和q
组成)。 -
c1
:第一段密文(使用number1
加密)。 -
c2
:第二段密文(使用e2 = 0xe18e
加密)。
目标:
-
解密
c1
得到第一部分明文。 -
解密
c2
得到第二部分明文(由于gcd(e2, φ(n)) = 2
,需要特殊处理)。 -
组合两段明文得到最终 flag。
2. 解题步骤
(1)分解 n 并计算 φ(n)
先将n分解出p,q
p = 102397419546952293033860597727650152144175130286102358700580521651161981691864932442389800376284315897109792547767071136122457986326994452907466660551539601
q = 196918114513369794295885764860865677200336789011735305193424080098388426330509485466134231492854453648288062591859752184850880742936527794052820501060652747
验证 n = p * q
:
n = p * q
assert n == number2, "分解正确!"
计算欧拉函数:
phi = (p - 1) * (q - 1)
(2)解密第一部分(标准 RSA)
假设 number1
是 e1
,计算私钥 d1
:
d1 = inverse(number1, phi)
m1 = pow(c1, d1, n)
part1 = long_to_bytes(m1).decode('utf-8', errors='ignore').strip('\x00')
输出:
第一部分明文: flag{75811c6d95770d
(3)解密第二部分(gcd(e2, φ(n)) = 2 的特殊情况)
由于 e2 = 57742
,计算 gcd(e2, phi)
:
g = math.gcd(e2, phi) # g = 2
解决方法:
-
计算调整后的
e2' = e2 // g = 28871
和φ' = φ(n) // g
。 -
计算
d2' = inverse(e2', φ')
。 -
计算部分解密结果
c2_prime = pow(c2, d2', n)
。 -
由于
g = 2
,需要对c2_prime
求模平方根才能得到最终明文。
代码:
e2_prime = e2 // g
phi_prime = phi // g
d2_prime = inverse(e2_prime, phi_prime)
c2_prime = pow(c2, d2_prime, n)
(4)求解模平方根
由于 n = p * q
,分别在 GF(p)
和 GF(q)
下求平方根,再用 CRT(中国剩余定理) 组合所有可能的解:
def get_square_roots(c, p):
try:
return sympy.ntheory.sqrt_mod(c, p, all_roots=True)
except ValueError:
return []
roots_p = get_square_roots(c2_prime, p) # 在模 p 下的解
roots_q = get_square_roots(c2_prime, q) # 在模 q 下的解
输出:
模 p 的平方根: [root1_p, root2_p]
模 q 的平方根: [root1_q, root2_q]
(5)用 CRT 组合所有可能的解
遍历所有可能的 (root_p, root_q)
组合,用 CRT 计算 m
:
from sympy.ntheory.modular import crt
valid_part2 = None
for mp in roots_p:
for mq in roots_q:
m, _ = crt([p, q], [mp, mq])
candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
if candidate.isprintable() and len(candidate) == 32: # 假设第二部分是 32 位哈希
valid_part2 = candidate
break
if valid_part2:
break
输出:
第二部分明文: 56092817b75f15df05}
(6)组合两段明文得到 flag
flag = part1 + valid_part2 + "}" # 补全 flag 的闭合括号
print("Final Flag:", flag)
最终 Flag:
flag{75811c6d95770d56092817b75f15df05}
3. 总结
-
标准 RSA 解密(第一部分):
-
直接计算
d1 = e1^{-1} mod φ(n)
,然后解密c1
。
-
-
非互素 RSA 解密(第二部分):
-
由于
gcd(e2, φ(n)) = 2
,需要:-
调整
e2' = e2 // 2
,计算d2'
。 -
解密后求模平方根。
-
-
-
模平方根计算:
-
分别在
GF(p)
和GF(q)
下计算,再用 CRT 组合。
-
-
验证明文:
-
检查解密结果是否为可打印字符(如 32 位 MD5 哈希)。
-
检查解密结果是否为可打印字符(如 32 位 MD5 哈希)。
-
4.完整代码
import sympy
from Crypto.Util.number import inverse, long_to_bytes
import math
# =============================================
# 给定参数(题目提供的数据)
# =============================================
number1 = 6035830951309638186877554194461701691293718312181839424149825035972373443231514869488117139554688905904333169357086297500189578624512573983935412622898726797379658795547168254487169419193859102095920229216279737921183786260128443133977458414094572688077140538467216150378641116223616640713960883880973572260683 # 可能是e1或d1
number2 = 20163906788220322201451577848491140709934459544530540491496316478863216041602438391240885798072944983762763612154204258364582429930908603435291338810293235475910630277814171079127000082991765275778402968190793371421104016122994314171387648385459262396767639666659583363742368765758097301899441819527512879933947 # 模数n
c1 = 6624758244437183700228793390575387439910775985543869953485120951825790403986028668723069396276896827302706342862776605008038149721097476152863529945095435498809442643082504012461883786296234960634593997098236558840899107452647003306820097771301898479134315680273315445282673421302058215601162967617943836306076 # 第一部分密文
c2 = 204384474875628990804496315735508023717499220909413449050868658084284187670628949761107184746708810539920536825856744947995442111688188562682921193868294477052992835394998910706435735040133361347697720913541458302074252626700854595868437809272878960638744881154520946183933043843588964174947340240510756356766 # 第二部分密文
# =============================================
# 预计算的质因数(通过分解n得到)
# =============================================
p = 102397419546952293033860597727650152144175130286102358700580521651161981691864932442389800376284315897109792547767071136122457986326994452907466660551539601
q = 196918114513369794295885764860865677200336789011735305193424080098388426330509485466134231492854453648288062591859752184850880742936527794052820501060652747
# 验证n = p*q
assert p * q == number2, "质因数分解验证失败!"
# =============================================
# 计算欧拉函数 φ(n) = (p-1)*(q-1)
# =============================================
phi = (p - 1) * (q - 1)
# =============================================
# 解密第一部分(标准RSA解密)
# =============================================
# 计算解密指数d1 = e1^(-1) mod φ(n)
d1 = inverse(number1, phi)
# 解密得到明文 m1 = c1^d1 mod n
m1 = pow(c1, d1, number2)
# 将长整数转换为字节并解码为字符串
part1 = long_to_bytes(m1).decode('utf-8', errors='ignore').strip('\x00')
print(f"[*] 第一部分解密结果: {part1}")
# =============================================
# 解密第二部分(e2与φ(n)不互质时的特殊情况)
# =============================================
e2 = 0xe18e # 57742(十六进制表示)
# 检查gcd(e2, φ(n)),如果不为1需要特殊处理
g = math.gcd(e2, phi)
print(f"[*] GCD(e2, phi) = {g} (需要特殊处理)")
# 计算调整后的参数
e2_prime = e2 // g # 使e2'与φ(n)'互质
phi_prime = phi // g # 缩减后的φ(n)'
d2_prime = inverse(e2_prime, phi_prime) # 计算部分解密指数
# 部分解密:c2_prime = c2^d2_prime mod n
# 注意:此时得到的不是最终明文,还需要解g次方根
c2_prime = pow(c2, d2_prime, number2)
# =============================================
# 求解模平方根(使用中国剩余定理)
# =============================================
def get_square_roots(c, modulus):
"""计算c的模平方根(支持质数模数)"""
try:
roots = sympy.ntheory.residue_ntheory.sqrt_mod(c, modulus, all_roots=True)
return roots
except ValueError:
return [] # 无解时返回空列表
# 分别在模p和模q下计算平方根
roots_p = get_square_roots(c2_prime, p) # 在模p下的解
roots_q = get_square_roots(c2_prime, q) # 在模q下的解
print(f"[*] 找到 {len(roots_p)} 个模p的根, {len(roots_q)} 个模q的根")
# 用中国剩余定理组合所有可能的解
from sympy.ntheory.modular import crt
valid_part2 = None
for mp in roots_p:
for mq in roots_q:
# 组合解:m ≡ mp mod p, m ≡ mq mod q
m, _ = crt([p, q], [mp, mq])
# 转换为字节并尝试解码
candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
# 验证候选明文是否有效(可打印字符或flag格式)
if all(ord(c) < 128 and (c.isprintable() or c == '\x00') for c in candidate):
valid_part2 = candidate
print(f"[+] 找到有效的第二部分: {valid_part2}")
break
if valid_part2:
break
# =============================================
# 结果输出
# =============================================
if valid_part2:
flag = part1 + valid_part2
print("\n=================================")
print(f"[★] 最终Flag: {flag}")
print("=================================")
else:
print("\n[!] 未能找到有效的第二部分明文")
print("尝试所有可能的组合:")
for i, mp in enumerate(roots_p):
for j, mq in enumerate(roots_q):
m, _ = crt([p, q], [mp, mq])
candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
print(f" 组合 {i}-{j}: {repr(candidate)}") # 使用repr显示不可见字符