蓝帽杯corrupted_key
一道综合的rsa.
题目如下
from Crypto.Util.number import *
key = RSA.generate(1024) open("flag.enc",'wb').write(PKCS1_OAEP.new(key.publickey()).encrypt(flag))
open('priv.pem','wb').write(key.exportKey('PEM'))
先是私钥分析。查了查资料,一个私钥文件包含标签头,n,e,d,p,q,dp,dq,inv_q%p. 将这些数据转化成16进制在经过base64加密,得到的就是pem私钥文件。
通过题目给出的文件 私钥解析可以详细看这篇
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDXFSUGqpzsBeUzXWtG9UkUB8MZn9UQkfH2Aw03YrngP0nJ3NwH
UFTgzBSLl0tBhUvZO07haiqHbuYgBegO+Aa3qjtksb+bH6dz41PQzbn/l4Pd1fXm
dJmtEPNh6TjQC4KmpMQqBTXF52cheY6GtFzUuNA7DX51wr6HZqHoQ73GQQIDAQAB
yQvOzxy6szWFheigQdGxAkEA4wFss2CcHWQ8FnQ5w7k4uIH0I38khg07HLhaYm1c
zUcmlk4PgnDWxN+ev+vMU45O5eGntzaO3lHsaukX9461mA==
-----END RSA PRIVATE KEY-----
我们可以得到 n,e,dq的低位,inv_q%p ,通过这些元素解方程,求出其他元素。
已知dq的低120位
原理同dp泄露。知道了dq,可以把q的低位求出。
dq = k ( dq-1 ) +1 ---> dq+k-1 = kp ----> 两边同时乘k在模2^120 的逆元 得到 k^-1 *(dq+k-1) = p (mod 2^120) 已知dq低位 那么可以枚举(1,e) 里的元素,求出k,从而求出q的低120位
通过sagemath 爆破 恢复q的高位,
q_low = [] for i in tqdm(range(e)): try: q0 = invert(i, 2 ** 120) * (e * dq_low + i - 1) % 2 ^ 120 q_low.append(q0) except: continue PR.<x> = Zmod(n)[] roots = [] q_low=q_low[29500:] for i in tqdm(range(len(q_low))): f = cf * (2^120*x + int(q_low[i])) ^ 2 - (2^120*x + int(q_low[i])) root = f.monic().small_roots(X = 2^(512-120))
这里取29500 是因为已经跑过一遍具体知道k的值,缩短遍历范围。得到了q就可以知道其他所有需要元素,找个常规脚本写就行。附上代码
#sagemath
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Util.number import *
from gmpy2 import *
from tqdm import tqdm
n = 0xd7152506aa9cec05e5335d6b46f5491407c3199fd51091f1f6030d3762b9e03f49c9dcdc075054e0cc148b974b41854bd93b4ee16a2a876ee62005e80ef806b7aa3b64b1bf9b1fa773e353d0cdb9ff9783ddd5f5e67499ad10f361e938d00b82a6a4c42a0535c5e76721798e86b45cd4b8d03b0d7e75c2be8766a1e843bdc641
e = 0x10001
cf = 0xe3016cb3609c1d643c167439c3b938b881f4237f24860d3b1cb85a626d5ccd4726964e0f8270d6c4df9ebfebcc538e4ee5e1a7b7368ede51ec6ae917f78eb598
dq_low = 0xc90bcecf1cbab3358585e8a041d1b1
q_low = []
for i in tqdm(range(e)):
try:
q0 = invert(i, 2 ** 120) * (e * dq_low + i - 1) % 2 ^ 120
q_low.append(q0)
except:
continue
PR.<x> = Zmod(n)[]
roots = []
q_low=q_low[29500:]
for i in tqdm(range(len(q_low))):
f = cf * (2^120*x + int(q_low[i])) ^ 2 - (2^120*x + int(q_low[i]))
root = f.monic().small_roots(X = 2^(512-120))
if root:
q = 2^120*int(root[0]) + int(q_low[i])
p = n // q
assert p * q == n
d = inverse(e, (p - 1) * (q - 1))
rsa = RSA.construct((int(n), int(e), int(d), int(p), int(q)))
# c = bytes_to_long(open('flag.enc', 'rb').read())
c = 96458723724899437870554342796876171017896652413964521193266438981853945238446913579867464909353925601873532290626111170073532116639383463734148270579305067733147411306325252107181823453497914478588342362177625026365513002442585949837516090367171824895036711246039928723021679235071368954348296729327873680822
c = long_to_bytes(c)
print(PKCS1_OAEP.new(rsa).decrypt(c))
把q求出来在python里跑也一样