CTF-CYRPTO-RSA-Recovery
RSA-Recovery
题目分析
- 私钥恢复
开始
1.题目
给出三个文件:
flag.enc、private.corrupted、pubkey.pem
很明显是加密结果、损坏的私钥、公钥。
2.分析
核心在于恢复损坏的私钥。Plaid CTF 2014、Jarvis OJ God Like RSA两题,都与此题类似。所以就直接借(zhao)鉴(chao)了。
PlaidCTF 2014 RSA writeup
RSA 私钥恢复和最优非对称加密填充
在此向大神表示崇高的敬意,我依然只能当个脚本小子。。。
3.私钥恢复
说实在话我根本没有看懂到底是在干嘛。这里有一个全过程的,可惜我依然看不懂,搬运过来给大家吧。
[Jarvis OJ] Crypto - God Like RSA
这位大神全程做下来。。。佩服。
(1)私钥结构
struct
{
BIGNUM *n; // public modulus
BIGNUM *e; // public exponent
BIGNUM *d; // private exponent
BIGNUM *p; // secret prime factor
BIGNUM *q; // secret prime factor
BIGNUM *dmp1; // d mod (p-1)
BIGNUM *dmq1; // d mod (q-1)
BIGNUM *iqmp; // q^-1 mod p
// ...
};
我们来看下损坏的私钥
modulus: //n
privateExponent: //d
prime1: //p
prime2: //q
exponent1: //dmp1,dpmask
exponent2: // *dmq1,dqmask
(2)恢复原理
原WP的内容是这样
- given a candidate for (p mod 16**(t - 1)), generate all possible candidates for (p mod 16**t) (check against mask for prime1)
- calculate q = n * invmod(p, 16**t) (and check against mask for prime2)
- calculate d = invmod(e, 16**t) * (1 + k * (N - p - q + 1)) (and check against mask for private exponent)
- calculate d_p = invmod(e, 16**t) * (1 + k_p * (p - 1)) (and check against mask for exponent1)
- calculate d_q = invmod(e, 16**t) * (1 + k_q * (q - 1)) (and check against mask for exponent2)
- if any of checks failed - check next candidate
简单翻译一下
- 给定一个(p mod 16**(t - 1))的候选对象,生成(p mod 16**t)的所有候选对象,用p的掩码来校验
- 计算q = n * invmod(p, 16**t),invmod是模逆,用q的掩码来校验
- 计算d = invmod(e, 16**t) * (1 + k * (N - p - q + 1)),用d的掩码来校验
- 计算d_p = invmod(e, 16**t) * (1 + k_p * (p - 1)),用dmp1的掩码来校验
- 计算d_q = invmod(e, 16**t) * (1 + k_q * (q - 1)) ,用dmq1的掩码来校验
- 如果其中任何一个校验失败,就尝试下一个候选对象。
这里所谓的掩码,其实就是把缺失的部分去掉拼起来(个人是这么理解的),便于比对。
(3)恢复脚本
先上脚本吧:
#!python3
# -*- coding: utf-8 -*-
# @Time : 2020/11/20 17:51
# @Author : A.James
# @FileName: test.py
import re
import pickle
from itertools import product
from libnum import invmod, gcd
def solve_linear(a, b, mod):
if a & 1 == 0 or b & 1 == 0:
return None
return (b * invmod(a, mod)) & (mod - 1) # hack for mod = power of 2
def to_n(s):
s = re.sub(r"[^0-9a-f]", "", s)
return int(s, 16)
#TODO:计算掩码,便于比对
def msk(s):
cleaned = "".join(map(lambda x: x[-2:], s.split(":")))
return msk_ranges(cleaned), msk_mask(cleaned), msk_val(cleaned)
def msk_ranges(s):
return [range