CTF misc crypto re pyjail总结
MISC
常见文件16进制头尾
一些CTF题目的附件会去掉文件头,需要补全文件头,在一些文件里也可能隐藏多个文件,这就需要熟悉文件尾/文件头以方便提取或做判断,题目附件在下载的时候可能会没有文件后缀,无法判断是那种文件,由于各文件的16进制都有规定格式,在将文件在 010Editor 中打开后根据文件16进制头可以判断是哪一种文件,总之需要熟悉常见文件头以及格式,010里面有模板,方便识别各类文件,但也会有识别出错的情况,MISC题目千变万化...
PNG
文件头 89 50 4E 47 0D 0A 1A 0A
文件尾 AE 42 60 82
**JPEG **
文件头 FF D8 FF
文件尾 FF D9
GIF
**文件头 47 49 46 38 39(37) 61
**
文件尾 00 3B
BMP
文件头 42 4D
ZIP
文件头 50 4B 03 04
**文件尾 50 4B 01 02 / 50 4B 05 06
**
RAR Archive (rar)
文件头:52 61 72 21
Wave (wav)
文件头:57 41 56 45
其他16进制
TGA
未压缩的前4字节 00 00 02 00
RLE压缩的前5字节 00 00 10 00 00
TIFF (tif)
文件头:49 49 2A 00
ico
文件头:00 00 01 00
MS Word/Excel (xls.or.doc)
文件头:D0 CF 11 E0
MS Access (mdb) 文件头:
53 74 61 6E 64 61 72 64 20 4A
WordPerfect (wpd) 文件头:
FF 57 50 43
Adobe Acrobat (pdf) 文件头:
25 50 44 46 2D 31 2E
application/vnd.visio(vsd) 文件头:
D0 CF 11 E0 A1 B1 1A E1
Email [thorough only] (eml) 文件头:
44 65 6C 69 76 65 72 79 2D 64 61 74 65 3A
Outlook Express (dbx) 文件头:
CF AD 12 FE C5 FD 74 6F
Outlook (pst) 文件头:
21 42 44 4E
Rich Text Format (rtf) 文件头:
7B 5C 72 74 66
txt 文件(txt) 文件头:Unicode:FE FF
/ Unicode big endian:FF FE
/ UTF-8:EF BB BF
/ANSI编码是没有文件头的
audio(Audio)
文件头: 4D 54 68 64
audio/x-aac(aac)
文件头:FF F1(9)
AVI (avi) 文件头:41 56 49 20
Real Audio (ram) 文件头:2E 72 61 FD
Real Media (rm) 文件头:2E 52 4D 46
MPEG (mpg) 文件头:00 00 01 BA(3)
Quicktime (mov) 文件头:6D 6F 6F 76
Windows Media (asf) 文件头:30 26 B2 75 8E 66 CF 11
MIDI (mid) 文件头:4D 54 68 64
XML (xml) 文件头:3C 3F 78 6D 6C
HTML (html) 文件头:68 74 6D 6C 3E
Quicken (qdf) 文件头:AC 9E BD 8F
Windows Password (pwl) 文件头:E3 82 85 96
windows证书文件(der) 文件头:30 82 03 C9
CAD (dwg) 文件头:41 43 31 30
Windows Shortcut (lnk) 文件头:4C 00 00 00
Windows reg(reg) 文件头:52 45 47 45 44 49 54 34
注:并非全部文件只收集常见,够用
隐写术
常用工具:
**16进制查看:010Editor winhex **
音频查看:Audacity
等等
遇到举例
png:
首先介绍一款好用的检测png隐写工具
zsteg:https://github.com/zed-0xff/zsteg
一些隐藏的文件,lsb隐写等都可以检测到
binwalk:
可以检测分离文件
#检测图片
binwalk filename
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 640 x 1743, 8-bit/color RGBA, non-interlaced
#分离图片
binwalk -e filename
(可能报错使用 binwlak -e filename --run-as=root)
#刀操作,也可以分离图片
dd if=filename of=newfilename skip=DECIMAL bs=1
foremost:
同样可以分离文件,而且细分化,有log,有时候有奇效
foremost -o 存放文件名 文件名
使用非常方便:
zsteg filename
或
zsteg filename -a
还有其他参数指令
zsteg -h
宽高隐写:
png文件最常见的就是宽高隐写,通过修改宽高隐藏一些内容,这类图片肉眼一般无法判断,有的倒非常明显,可以通过修改宽高的16进制来尝试恢复,但有可能无法得到预期图片,可以通过工具或者crc爆破得到正确的宽高再修改文件的16进制得到原来的图片。
文件16进制中:
隐藏的信息可能存放在16进制文件尾,使用010打开在末尾处查看,也可能存放在文件中,使用搜索尝试寻找
010功能强大
IDAT隐写:
pngcheck可以检测文件IDAT,损坏,多余
文件IDAT通常中间的会保持一致再满了的时候才会进行下一个
此时就需要提取或者删除来达到恢复的目的
里面通常会有zilb,也可能隐写在这里面
StegSolve:一款工具
png jpg gif都可以使用,检测图片lsb隐写,各颜色通道等
Data Extract:lsb隐写等
Frame Browser:gif帧分析
exiftool:
查看图片exif等信息,flag可能隐藏在这里
PNGdebugger/TweakPNG(png工具):
均可查看png文件数据,支持修改删除等操作
stegpy:
带有key的隐写
stegpy filename -p
png相关隐写暂时这么多
JPEG/JPG:
EXIF:
最常见的就是EXIF,备注,详细信息等,会有提示信息或者flag,
使用exiftool:
exiftool filename
文件16进制:
同png一样,也可能存放在末尾或者文件中。
文件提取:
存在附件文件,使用binwalk/foremost
JSteg&&JPHide&&OutGuess&&F5:
四个隐写方式
可以使用相应工具提取
Stegdetect 可以帮助探测
SilentEye:
jpg或者wav文件隐写flag
一些非预期(strings filename)
音频隐写:
MP3/WAV:
使用Audacity查看波形图或者频谱图,一些信息会隐藏在上面,波形图的摩斯密码,频谱图可能有key,pass,甚至flag等
MP3Stego:MP3隐写工具
encode -E txt -P 密码 MP3文件
decode -X -P 密码 MP3name
这种一般会有pass提示在文件尾等
LSB:DerbyCon CTF - WAV Steganography · ethackal
deepsound:WAV工具
需要密码
dtmf2num:拨号音
音频滴滴滴
MMSSTV:
一种电报,可以将音频转化为图片,音频多嘈杂
SilentEye:wav工具
xls:
文件可能存在隐写有二维码,手点检测块是否不同,将其黑白变化得到二维码,可以使用QR扫描得到flag
使用7z点开里面存在内容,这类不好找可以使用程序寻找等
word:
字体隐藏:ctrl+a修改全局字体得到隐藏内容,修改设置得到隐藏文字,修改后缀或使用7z打开得到里面的文件,可能存在flag等有用信息
pyc隐写:
使用剑龙隐写可以得到隐藏的flag
stegosaurus -x pycname
CRYPTO
常见编码:
rabbit/aes/des加密:
U2FsdGVkX1通常为rabbit密文头,特征类似base64(aes ,des相似)
rabbit为一种流密码,其详情高速流密码——Rabbit - 掘金 (juejin.cn)
在线解密网:在线Rabbit加密 | Rabbit解密- 在线工具 (sojson.com)
例子:
U2FsdGVkX188nKW3XYAppATn9y3lZg== 解密为:flag
base家族:
(base64是每6bit位一个映射base32是每5bit位一个映射base16是每bit4位一个映射)
base64加密:( 编码后的数据比原始数据略长,为原来的4/3)
密文结尾通常为,由(A-Z、a-z、0-9、+、/)组成,以为填充
加密原理:用64个可打印字符表示二进制所有数据的方法...(Base64 算法原理,以及编码、解码【加密、解密】 介绍 - 程默 - 博客园 (cnblogs.com))
例子:
ZmxhZ3tiYXNlfQ== 解密为:flag{base}
在线解密网:Base64 编码/解码 - 在线工具 (toolhelper.cn)
base58加密:(在比特币加密中有涉及,区块链)
base58的编码表相比base64少了数字0,大写字母I,O,小写字母 l (这个是L),以及符号‘+’和‘/’
加密原理:将字符流转成256进制的一个超大数 然后不断的模58
例子:
2w6zoHymm2SU4UfhE 解密为: flag{base58}
在线解密网:在线Base58编码解码 (lddgo.net)
base32加密:
由(A-Z、2-7)32个可见字符构成,“=”符号用作后缀填充,无数字+/,会有多个=。
例子:
MZWGCZ33MJQXGZJTGJ6Q==== 解密为: flag{base32}
在线解密网:Base32编码解码,Base32在线转换工具 - 千千秀字 (qqxiuzi.cn)
base16加密:
将二进制文件转换成由16个字符组成的文本,由a-z,0-9组成,无=填充,与md5,hex相似。
例子:
666c61677b6261736531367d 解密为:flag{base16}
在线解密网:Base16编码解码,Base16在线转换工具 - 千千秀字 (qqxiuzi.cn)
除此之外还有
base62,base85,base91,base92,base100。
base91的密文由91个字符(0-9,a-z,A-Z,!#$%&()*+,./:;<=>?@[]^_`{|}~”)组成
base100为表情包
base85为BOu!rDst>tGAhM<A1fSl1GgsI特征:特点是奇怪的字符比较多,但是很难出现等号
等等十分多
这里推荐一个工具ToolsFx非常多内容,编码(CTF站点导航 | 猫捉鱼铃 (mzy0.com))
古典密码还有非常多类型:
推荐网站:CTF常见编码及加解密(超全) - ruoli-s - 博客园 (cnblogs.com)
一些好用的加密解密网站:
爆破维吉尼亚:Vigenere Solver | guballa.de
佛曰http://hi.pcmoe.net/buddha.html
盲文:http://www.atoolbox.net/Tool.php?Id=837
RSA:
推荐大佬博客:https://tangcuxiaojikuai.xyz/
神 糖醋小鸡快
from Crypto.Util.number import *
n = p * q
m = "flag"
m = bytes_to_long(m)
e = 65537
phi = (p-1)*(q-1)
d = inverse(e,phi)
c = pow(m,e,n)
m = pow(c,d,n)
flag = long_to_bytes(m)
一些常见攻击方式的脚本和方法:
【CTF-RSA】基于N分解的RSA题目
1.在线查询分解网站
http://www.factordb.com/index.php
2.使用yafu工具分解
下载地址:https://sourceforge.net/projects/yafu/
3.使用费马分解
网上找的脚本,p和q接近
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
def fermat(n, verbose=True):
a = isqrt(n) # int(ceil(n**0.5))
b2 = a*a - n
b = isqrt(n) # int(b2**0.5)
count = 0
while b*b != b2:
# if verbose:
# print('Trying: a=%s b2=%s b=%s' % (a, b2, b))
a = a + 1
b2 = a*a - n
b = isqrt(b2) # int(b2**0.5)
count += 1
p=a+b
q=a-b
assert n == p * q
# print('a=',a)
# print('b=',b)
# print('p=',p)
# print('q=',q)
# print('pq=',p*q)
return p, q
fermat(n)
4.分解出来后,用脚本解密即可
import gmpy2
import libnum
p=
q=
e=
c=
n=p*q
phi_n=(p-1)*(q-1)
#求逆元
#d=libnum.invmod(e,phi_n)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode())
出题脚本
p,q接近,很快就能分解
import libnum
import gmpy2
p=libnum.generate_prime(1024)
#下一个素数
q=gmpy2.next_prime(p)
print(p)
print(q)
print(gmpy2.is_prime(q))
e=65537
m="flag{20d6e2da95dcc1fa5f5432a436c4be18}"
m=libnum.s2n(m)
n=p*q
phi_n=(p-1)*(q-1)
d=libnum.invmod(e,phi_n)
c=pow(m,e,n)
print("n=",n)
print ("e=",e)
print ("c=",c)
解题脚本
import gmpy2
import libnum
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
def fermat(n, verbose=True):
a = isqrt(n) # int(ceil(n**0.5))
b2 = a*a - n
b = isqrt(n) # int(b2**0.5)
count = 0
while b*b != b2:
# if verbose:
# print('Trying: a=%s b2=%s b=%s' % (a, b2, b))
a = a + 1
b2 = a*a - n
b = isqrt(b2) # int(b2**0.5)
count += 1
p=a+b
q=a-b
assert n == p * q
# print('a=',a)
# print('b=',b)
print('p=',p)
print('q=',q)
# print('pq=',p*q)
return p, q
n=
e= 65537
c=
pq=fermat(n)
p=pq[0]
q=pq[1]
phi_n=(p-1)*(q-1)
#求逆元
#d=libnum.invmod(e,phi_n)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode())
RSA密钥生成与读取
from Crypto.PublicKey import RSA
p=
q=
n=
d=
e= 65537
rsa_components = (n, e)
keypair = RSA.construct(rsa_components)
with open('pubckey.pem', 'wb') as f :
f.write(keypair.exportKey())
from Crypto.PublicKey import RSA
p= 787228223375328491232514653709
q= 814212346998672554509751911073
n= 640970939378021470187479083920100737340912672709639557619757
d= 590103645243332826117029128695341159496883001869370080307201
e= 65537
rsa_components = (n,e,d,p,q)
keypair = RSA.construct(rsa_components)
with open('private1.pem', 'wb') as f :
f.write(keypair.exportKey())
公钥读取
from Crypto.PublicKey import RSA
with open("pubckey.pem","rb") as f:
key = RSA.import_key(f.read())
print('n = %d' % key.n)
print('e = %d' % key.e)
私钥读取
from Crypto.PublicKey import RSA
with open("private1.pem","rb") as f:
key = RSA.import_key(f.read())
print('n = %d' % key.n)
print('e = %d' % key.e)
print('d = %d' % key.d)
print('p = %d' % key.p)
print('q = %d' % key.q)
出题脚本 -基于N分解的题目
import libnum
import gmpy2
from Crypto.PublicKey import RSA
p=libnum.generate_prime(1024)
#下一个素数
q=int(gmpy2.next_prime(p))
e=65537
m="flag{}"
m=libnum.s2n(m)
n=p*q
c=pow(m,e,n)
flag_c=libnum.n2s(c)
rsa_components = (n, e)
keypair = RSA.construct(rsa_components)
with open('pubckey1.pem', 'wb') as f :
f.write(keypair.exportKey())
with open("flag.txt","wb") as f:
f.write(flag_c)
解题脚本
import libnum
import gmpy2
from Crypto.PublicKey import RSA
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
def fermat(n, verbose=True):
a = isqrt(n) # int(ceil(n**0.5))
b2 = a*a - n
b = isqrt(n) # int(b2**0.5)
count = 0
while b*b != b2:
# if verbose:
# print('Trying: a=%s b2=%s b=%s' % (a, b2, b))
a = a + 1
b2 = a*a - n
b = isqrt(b2) # int(b2**0.5)
count += 1
p=a+b
q=a-b
assert n == p * q
# print('a=',a)
# print('b=',b)
# print('p=',p)
# print('q=',q)
# print('pq=',p*q)
return p, q
with open("pubckey1.pem","rb") as f:
key = RSA.import_key(f.read())
n=key.n
e=key.e
with open("flag.txt","rb") as f:
c=f.read()
c=libnum.s2n(c)
#费马分解,
n1=fermat(n)
p=n1[0]
q=n1[1]
phi_n=(p-1)*(q-1)
#求逆元
d=libnum.invmod(e,phi_n)
m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode())
进阶——自动生成密钥及加解密
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
random_generator = Random.new().read
rsa = RSA.generate(2048, random_generator)
生成私钥
private_key = rsa.exportKey()
# print(private_key.decode('utf-8'))
with open('rsa_private_key.pem', 'wb')as f:
f.write(private_key)
生成公钥
public_key = rsa.publickey().exportKey()
# print(public_key.decode('utf-8'))
with open('rsa_public_key.pem', 'wb')as f:
f.write(public_key)
#测试用密钥加密
public_key = RSA.importKey(public_key)
msg='flag'
pk = PKCS1_v1_5.new(public_key)
encrypt_text = pk.encrypt(msg.encode())
print(encrypt_text)
#测试密钥解密
private_key = RSA.importKey(private_key)
pk = PKCS1_v1_5.new(private_key)
msg = pk.decrypt(encrypt_text,0)
print(msg)
#两种标准
rsa_components = (n, e, int(d), p, q)
arsa = RSA.construct(rsa_components)
rsakey = RSA.importKey(arsa.exportKey())
rsakey = PKCS1_OAEP.new(rsakey)
decrypted = rsakey.decrypt(c)
print(decrypted)
共模攻击脚本
解密脚本
#coding:utf-8
import gmpy2
import libnum
n=
e1=
c1=
e2=
c2=
#共模攻击
#共模攻击函数
def rsa_gong_N_def(e1,e2,c1,c2,n):
e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n)
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n
return int(m)
m = rsa_gong_N_def(e1,e2,c1,c2,n)
print(m)
print(libnum.n2s(int(m)))
共模攻击原理
两个及以上的公钥(n,e)来加密同一条信息m
c1 = pow(m, e1, n)
c2 = pow(m, e2, n)
e1,e2互质,则有
gcd(e1,e2)=1
根据扩展欧几里德算法 对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整数 x,y 使得 gcd(a,b)=ax+by
e1*s1+e2*s2 = 1
s1、s2皆为整数,但是一正一负,假设s1为正数,s2为负数
因为
c1 = m^e1%n
c2 = m^e2%n
可得:
(c1^s1*c2^s2)%n = ((m^e1%n)^s1(m^e2%n)^s2)%n
根据模运算性质: 幂运算是一种关于幂的数学运算。同底数幂相乘,底数不变,指数相加。同底数幂相除,底数不变,指数相减。幂的乘方,底数不变,指数相乘。
(a * b) % p = (a % p * b % p) % p
a ^ b % p = ((a % p) ^ b) % p
简化公式为:
(c1^s1*c2^s2)%n = ((m^e1%n)^s1*(m^e2%n)^s2)%n
=> (c1^s1*c2^s2)%n = ((m^e1%n)^s1%n*(m^e2%n)^s2%n)%n #(a * b) % p = (a % p * b % p) % p
=> (c1^s1*c2^s2)%n = ((m^e1)^s1%n*(m^e2)^s2%n)%n #((a % p) ^ b) % p =a ^ b % p
=> (c1^s1*c2^s2)%n = ((m^e1)^s1*(m^e2)^s2)%n # (a % p * b % p) % p=(a * b) % p
=>(c1^s1*c2^s2)%n = ((m^(e1*s1)*(m^(e2*s2))%n #。幂的乘方,底数不变,指数相乘。
(c1^s1*c2^s2)%n = (m^(e1*s1+e2*s2))%n # 同底数幂相乘,底数不变,指数相加。
因为 e1*s1+e2*s2 = 1 得:
(c1^s1*c2^s2)%n = (m^1)%n
(c1^s1*c2^s2)%n=m
同一m,同一n,不同e,进行加密。在不知道d的情况下,可以进行解密
wiener(维纳)攻击脚本
解密脚本
import gmpy2
import libnum
def continuedFra(x, y):
"""计算连分数
:param x: 分子
:param y: 分母
:return: 连分数列表
"""
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf
def gradualFra(cf):
"""计算传入列表最后的渐进分数
:param cf: 连分数列表
:return: 该列表最后的渐近分数
"""
numerator = 0
denominator = 1
for x in cf[::-1]:
# 这里的渐进分数分子分母要分开
numerator, denominator = denominator, x * denominator + numerator
return numerator, denominator
def solve_pq(a, b, c):
"""使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
:param a:x^2的系数
:param b:x的系数
:param c:pq
:return:p,q
"""
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
"""计算列表所有的渐近分数
:param cf: 连分数列表
:return: 该列表所有的渐近分数
"""
gf = []
for i in range(1, len(cf) + 1):
gf.append(gradualFra(cf[:i]))
return gf
def wienerAttack(e, n):
"""
:param e:
:param n:
:return: 私钥d
"""
cf = continuedFra(e, n)
gf = getGradualFra(cf)
for d, k in gf:
if k == 0: continue
if (e * d - 1) % k != 0:
continue
phi = (e * d - 1) // k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return d
n=
e=
c=
d=wienerAttack(e, n)
m=pow(c, d, n)
print(libnum.n2s(m).decode())
低加密指数广播攻击
import libnum
from gmpy2 import invert, gcd, iroot
def op(x):
res = 1
for i in x:
res *= i
return res
def CRT(m, a):
assert (len(m) == len(a))
M = op(m)
sum = 0
for m, a in zip(m, a):
Mi = M // m
ti = invert(Mi, m)
sum += a * ti * Mi
return sum % M
def GCRT(m, a):
assert (len(m) == len(a))
curm, cura = m[0], a[0]
for m, a in zip(m[1:], a[1:]):
d = gcd(curm, m)
c = a - cura
assert (c % d == 0)
K = c // d * invert(curm // d, m // d)
cura += curm * K
curm = curm * m // d
return cura % curm
e= 23
n= [, , , , , , ]
c= [, , , , , , ]
m = CRT(n, c)
m1 = iroot(m, e) # 开e次方
print(m1)
print(libnum.n2s(int(m1[0])))
解密脚本2
import binascii,gmpy2
from functools import reduce
import libnum
def CRT(mi, ai):
assert(reduce(gmpy2.gcd,mi)==1)
assert (isinstance(mi, list) and isinstance(ai, list))
M = reduce(lambda x, y: x * y, mi)
ai_ti_Mi = [a * (M // m) * gmpy2.invert(M // m, m) for (m, a) in zip(mi, ai)]
return reduce(lambda x, y: x + y, ai_ti_Mi) % M
e= 23
n= [, , , , , , ]
c= [, , , , , , ]
m=gmpy2.iroot(CRT(n, c), e)[0]
print(m)
print(libnum.n2s(int(m)))
N不互素(共享素数)
解题脚本
import libnum
import gmpy2
e= 65537
n1=
n2=
c1=
c2=
#求最大公约数
q=gmpy2.gcd(n1,n2)
p1=n1//q
phi_n=(q-1)*(p1-1)
#求逆元d
d1=libnum.invmod(e,phi_n)
m=pow(c1,d1,n1)
print(m)
#数字转字节,转字符串
print(libnum.n2s(int(m)).decode())
dp泄露
解密脚本
#coding:utf-8
import libnum
import gmpy2
n=
e= 65537
dp=
c=
for i in range(1,65535):
p=(dp*e-1)//i+1
if n%p==0:
q=n//p
break
print(p)
print(q)
phi_n= (p-1)*(q-1)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
print(m)
flag=libnum.n2s(int(m)).decode()
print(flag)
dp,dq
解题脚本
# coding=utf-8
import gmpy2
import libnum
def decrypt(dp,dq,p,q,c):
InvQ = gmpy2.invert(q, p)
mp = pow(c, dp, p)
mq = pow(c, dq, q)
m = (((mp-mq)*InvQ) % p)*q+mq
print(libnum.n2s(int(m)).decode())
p=
q=
dq=
dp=
c=
decrypt(dp,dq,p,q,c)
n是p的r次方
解密脚本
先分解n
import libnum import gmpy2
n=
e= 65537
c= #分解n #yafu-x64.exe factor() p=
phi_n=p**4-p**3 #求逆元
d=libnum.invmod(e,phi_n)
m=pow(c,d,n)
print(m) #数字转字节,转字符串
print(libnum.n2s(int(m)).decode())
RSA NC不互素
解题脚本
import gmpy2
import libnum
n =
c =
e = 0x10001
p = gmpy2.gcd(n, c)
q = n // p
assert n == p * q
phi_n=(p-1)*(q-1)
d=gmpy2.invert(e,phi_n)
M=pow(c,d,n)
#M= 2021 * 1001 * p*m
m=M//(2021 * 1001 * p)
print(libnum.n2s(int(m)))
sage脚本_p高位攻击
脚本1
根据题目,注意 2^60 60需要修改相应的位数 a = (p >> 60) << 60
def phase3(high_p, n):
R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
p = high_p + x
x0 = p.small_roots(X = 2^60, beta = 0.1)[0]
P = int(p(x0))
Q = n // P
print(P)
print(Q)
assert n == P*Q
n=
p4=
e=
c=
phase3(p4, n)
脚本2
n =
p4=
e = 0x10001
pbits = 1024
kbits = pbits - p4.nbits()
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:
p = p4+int(roots[0])
print ("n: ", n)
print ("p: ", p)
print ("q: ", n/p)
sage脚本_m高位攻击
解题脚本
import libnum
def phase2(high_m, n, c):
R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
m = high_m + x
M = m((m^3 - c).small_roots()[0])
print(libnum.n2s(int(M)))
n=
e= 3
c=
high_m=
phase2(high_m, n, c)
sage脚本_Franklin-Reiter attack
#针对RSA的Franklin Reiter攻击
https://paper.seebug.org/727/#43-franklin-reiter
#如果两条消息之间仅存在已知的固定差异
m1 = bytes_to_long(flag)
m2 = a*m1 + b
#和RSA是否在相同的RSA模N下加密
#这样就有可能同时恢复它们
from secret import flag
from Crypto.Util.number import *
m1 = bytes_to_long(flag)
N = getPrime(512)*getPrime(512)
e = 17
c1 = pow(m1, e, N)
a = getRandomNBitInteger(512)
b = getRandomNBitInteger(512)
m2 = a*m1 + b
c2 = pow(m2, e, N)
print(N, a, b, c1, c2, sep="\n")
sage解题脚本
https://github.com/ValarDragon/CTF-Crypto/blob/master/RSA/FranklinReiter.sage
n=
a=
b=
c1=
c2=
e=17
import libnum
def franklinReiter(n,e,c1,c2,a,b):
R.<X> = Zmod(n)[]
f1 = X^e - c1
f2 = (X*a+ b)^e - c2
# coefficient 0 = -m, which is what we wanted!
return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0])
# GCD is not implemented for rings over composite modulus in Sage
# so we do our own implementation. Its the exact same as standard GCD, but with
# the polynomials monic representation
def compositeModulusGCD(a, b):
if(b == 0):
return a.monic()
else:
return compositeModulusGCD(b, a % b)
m=franklinReiter(n,e,c1,c2,a,b)
print(m)
print(type(m))
print(libnum.n2s(int(m)))
sage脚本_d 低位攻击
import libnum
def getFullP(low_p, n):
R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
p = x*2^512 + low_p
root = (p-n).monic().small_roots(X = 2^128, beta = 0.4)
if root: return p(root[0]) return None
def phase4(low_d, n, c):
maybe_p = []
for k in range(1, 4):
p = var('p')
p0 = solve_mod([3*p*low_d == p + k*(n*p - p^2 - n + p)], 2^512)
maybe_p += [int(x[0])
for x in p0] #print(maybe_p)
for x in maybe_p:
P = getFullP(x, n)
if P:
break
P = int(P)
Q = n // P
assert P*Q == n
print(P)
print(Q)
d = inverse_mod(3, (P-1)*(Q-1))
print(d)
print(libnum.n2s(int(power_mod(c, d, n))))
n =
c =
low_d =
phase4(low_d, n, c)
sage脚本_广播攻击
import libnum
e=13
n1=
n2=
n3=
c1=
c2=
c3=
def phase5(n1, c1, n2, c2, n3, c3):
r = CRT([c1, c2, c3], [n1, n2, n3])
m = int(r)^(1/13)
print(m)
print(libnum.n2s(int(m)))
phase5(n1, c1, n2, c2, n3, c3)
sage脚本_Boneh Durfee 攻击
跟维纳攻击类似 用于e很大的情况
import time
"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True
"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False
"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 # stop removing if lattice reaches that dimension
############################################
# Functions
##########################################
# display stats on helpful vectors
def helpful_vectors(BB, modulus):
nothelpful = 0
for ii in range(BB.dimensions()[0]):
if BB[ii,ii] >= modulus:
nothelpful += 1
print (nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
# display matrix picture with 0 and X
def matrix_overview(BB, bound):
for ii in range(BB.dimensions()[0]):
a = ('%02d ' % ii)
for jj in range(BB.dimensions()[1]):
a += '0' if BB[ii,jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
if BB[ii, ii] >= bound:
a += '~'
print (a)
# tries to remove unhelpful vectors
# we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
# end of our recursive function
if current == -1 or BB.dimensions()[0] <= dimension_min:
return BB
# we start by checking from the end
for ii in range(current, -1, -1):
# if it is unhelpful:
if BB[ii, ii] >= bound:
affected_vectors = 0
affected_vector_index = 0
# let's check if it affects other vectors
for jj in range(ii + 1, BB.dimensions()[0]):
# if another vector is affected:
# we increase the count
if BB[jj, ii] != 0:
affected_vectors += 1
affected_vector_index = jj
# level:0
# if no other vectors end up affected
# we remove it
if affected_vectors == 0:
print ("* removing unhelpful vector", ii)
BB = BB.delete_columns([ii])
BB = BB.delete_rows([ii])
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# level:1
# if just one was affected we check
# if it is affecting someone else
elif affected_vectors == 1:
affected_deeper = True
for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
# if it is affecting even one vector
# we give up on this one
if BB[kk, affected_vector_index] != 0:
affected_deeper = False
# remove both it if no other vector was affected and
# this helpful vector is not helpful enough
# compared to our unhelpful one
if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
BB = BB.delete_columns([affected_vector_index, ii])
BB = BB.delete_rows([affected_vector_index, ii])
monomials.pop(affected_vector_index)
monomials.pop(ii)
BB = remove_unhelpful(BB, monomials, bound, ii-1)
return BB
# nothing happened
return BB
"""
Returns:
* 0,0 if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
"""
Boneh and Durfee revisited by Herrmann and May
finds a solution if:
* d < N^delta
* |x| < e^delta
* |y| < e^0.5
whenever delta < 1 - sqrt(2)/2 ~ 0.292
"""
# substitution (Herrman and May)
PR.<u, x, y> = PolynomialRing(ZZ)
Q = PR.quotient(x*y + 1 - u) # u = xy + 1
polZ = Q(pol).lift()
UU = XX*YY + 1
# x-shifts
gg = []
for kk in range(mm + 1):
for ii in range(mm - kk + 1):
xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
gg.append(xshift)
gg.sort()
# x-shifts list of monomials
monomials = []
for polynomial in gg:
for monomial in polynomial.monomials():
if monomial not in monomials:
monomials.append(monomial)
monomials.sort()
# y-shifts (selected by Herrman and May)
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
yshift = Q(yshift).lift()
gg.append(yshift) # substitution
# y-shifts list of monomials
for jj in range(1, tt + 1):
for kk in range(floor(mm/tt) * jj, mm + 1):
monomials.append(u^kk * y^jj)
# construct lattice B
nn = len(monomials)
BB = Matrix(ZZ, nn)
for ii in range(nn):
BB[ii, 0] = gg[ii](0, 0, 0)
for jj in range(1, ii + 1):
if monomials[jj] in gg[ii].monomials():
BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
# Prototype to reduce the lattice
if helpful_only:
# automatically remove
BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
# reset dimension
nn = BB.dimensions()[0]
if nn == 0:
print ("failure")
return 0,0
# check if vectors are helpful
if debug:
helpful_vectors(BB, modulus^mm)
# check if determinant is correctly bounded
det = BB.det()
bound = modulus^(mm*nn)
if det >= bound:
print ("We do not have det < bound. Solutions might not be found.")
print ("Try with highers m and t.")
if debug:
diff = (log(det) - log(bound)) / log(2)
print ("size det(L) - size e^(m*n) = ", floor(diff))
if strict:
return -1, -1
else:
print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
# display the lattice basis
if debug:
matrix_overview(BB, modulus^mm)
# LLL
if debug:
print ("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug:
print ("LLL is done!")
# transform vector i & j -> polynomials 1 & 2
if debug:
print ("looking for independent vectors in the lattice")
found_polynomials = False
for pol1_idx in range(nn - 1):
for pol2_idx in range(pol1_idx + 1, nn):
# for i and j, create the two polynomials
PR.<w,z> = PolynomialRing(ZZ)
pol1 = pol2 = 0
for jj in range(nn):
pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
# resultant
PR.<q> = PolynomialRing(ZZ)
rr = pol1.resultant(pol2)
# are these good polynomials?
if rr.is_zero() or rr.monomials() == [1]:
continue
else:
print ("found them, using vectors", pol1_idx, "and", pol2_idx)
found_polynomials = True
break
if found_polynomials:
break
if not found_polynomials:
print ("no independant vectors could be found. This should very rarely happen...")
return 0, 0
rr = rr(q, q)
# solutions
soly = rr.roots()
if len(soly) == 0:
print ("Your prediction (delta) is too small")
return 0, 0
soly = soly[0][0]
ss = pol1(q, soly)
solx = ss.roots()[0][0]
#
return solx, soly
def example():
############################################
# How To Use This Script
##########################################
#
# The problem to solve (edit the following values)
#
# the modulus
N =
# the public exponent
e =
# the hypothesis on the private exponent (the theoretical maximum is 0.292)
delta = 0.280 # this means that d < N^delta
#
# Lattice (tweak those values)
#
# you should tweak this (after a first run), (e.g. increment it until a solution is found)
m = 4 # size of the lattice (bigger the better/slower)
# you need to be a lattice master to tweak these
t = int((1-2*delta) * m) # optimization from Herrmann and May
X = 2*floor(N^delta) # this _might_ be too much
Y = floor(N^(1/2)) # correct if p, q are ~ same size
#
# Don't touch anything below
#
# Problem put in equation
P.<x,y> = PolynomialRing(ZZ)
A = int((N+1)/2)
pol = 1 + x * (A + y)
#
# Find the solutions!
#
# Checking bounds
if debug:
print ("=== checking values ===")
print ("* delta:", delta)
print ("* delta < 0.292", delta < 0.292)
print ("* size of e:", int(log(e)/log(2)))
print ("* size of N:", int(log(N)/log(2)))
print ("* m:", m, ", t:", t)
# boneh_durfee
if debug:
print ("=== running algorithm ===")
start_time = time.time()
solx, soly = boneh_durfee(pol, e, m, t, X, Y)
# found a solution?
if solx > 0:
print ("=== solution found ===")
if False:
print ("x:", solx)
print ("y:", soly)
d = int(pol(solx, soly) / e)
print ("private key found:", d)
else:
print ("=== no solution was found ===")
if debug:
print("=== %s seconds ===" % (time.time() - start_time))
if __name__ == "__main__":
example()
sage脚本_维纳变形(三素数pqr)
这一段代码求d
def wiener(e, n):
m = 12345
c = pow(m, e, n)
q0 = 1
list1 = continued_fraction(Integer(e) / Integer(n))
conv = list1.convergents()
for i in conv:
k = i.numerator()
q1 = i.denominator()
for r in range(20):
for s in range(20):
d = r * q1 + s * q0
m1 = pow(c, d, n)
if m1 == m:
return d
q0 = q1
c =
e =
n =
d=wiener(e, n)
print(d)
这一段代码是完整求flag的
from Crypto.Util.number import *
import gmpy2
import math
import libnum
c =
e =
n =
def plus(e, n):
m = 2
c = pow(m, e, n)
q0 = 1
list1 = continued_fraction(Integer(e)/Integer(n))
conv = list1.convergents()
for i in conv:
k = i.numerator()
#print(k)
q1 = i.denominator()
#print(q1)
for r in range(20):
for s in range(20):
d = r*q1 + s*q0
m1 = pow(c, d, n)
if m1 == m:
print(r,s)
return d
q0 = q1
d = plus(e, n)
print(d)
print(libnum.n2s(int(pow(c,d,n))))
PEM证书格式
-
证书解Base64后再转换为16进制输出
from base64 import b64decode import binascii s = '''MIICWwIBAAKBgHBkeHn6Q67opdN4V1S3mI0SsUuYzzm+IbXZDz4yZOMWz5nDBYuJ SA8rRqDtqb7mtNdTGZZx7xe6tOwleqvkXn629mgUZZegyaBdBSPnUR6IUqduqpLo HRavrHr1IkI6oAmDEQzi1lCZ03x0jMKuoOKp9LBhP9ijCoy9iRh9tH+FAgMBAAEC gYAvLn5E9oKjUpcKh2Jh9hDcaBR1n9iebOrJ5C059v3TNyg/bFdPlHnjpE8qD5tK wJ76JbCAL6QnWgHJgJJWxq/EAy/9SG+eApaBo94Sb2B2A1WceDf8F1idkXUOvU/3 kd/wbw/gLZLya8WCFF4SUZx09TToMqSWDEJI4kN17pU5AQJBAJv9ShfSbaMNK31O kg9LSI7wFLq7iiFRl5kXvSKLsYB3HAKHNlV6/ZL1TV2jg37yf9Mi2f0Gx5AVXbwi /1ef9R0CQQC4c5EkR8VAw8lqePLwCkCJisXKAEOqPZFOiSCCIVnY+5J4kgiZiS33 rskWwsYAIBHVKiXSy+5NdvWk51MeYi+JAkBiZNvmuOJVVkpXaUcyhH9JQmEhBIj1 yVzBwbqY3trhOMCfS6DXPJRUrYzWgvzAB8Dfcn1kYHFjDkcpFD5SjGB1AkBtyKNH w8v820tjqu91vbRh6Q4GSBf+GL0G0IlfyrfudPXd+5VQxRxuAkM/39f3tR7IEFkI 2UZSJw7YArMvL2N5AkEAmVGTH6DU3ygzjCtdl4/2dhonSHcEovCFWGZuCqBjYEw6 IGYAlpOiv/BICMXrOBsdd1+4j6n1edxHSGH9q4Aoug==''' s = b64decode(s) print(binascii.hexlify(s))
得到
3082025b02010002818070647879fa43aee8a5d3785754b7988d12b14b98cf39be21b5d90f3e3264e316cf99c3058b89480f2b46a0eda9bee6b4d753199671ef17bab4ec257aabe45e7eb6f668146597a0c9a05d0523e7511e8852a76eaa92e81d16afac7af522423aa00983110ce2d65099d37c748cc2aea0e2a9f4b0613fd8a30a8cbd89187db47f8502030100010281802f2e7e44f682a352970a876261f610dc6814759fd89e6ceac9e42d39f6fdd337283f6c574f9479e3a44f2a0f9b4ac09efa25b0802fa4275a01c9809256c6afc4032ffd486f9e029681a3de126f607603559c7837fc17589d91750ebd4ff791dff06f0fe02d92f26bc582145e12519c74f534e832a4960c4248e24375ee9539010241009bfd4a17d26da30d2b7d4e920f4b488ef014babb8a2151979917bd228bb180771c028736557afd92f54d5da3837ef27fd322d9fd06c790155dbc22ff579ff51d024100b873912447c540c3c96a78f2f00a40898ac5ca0043aa3d914e8920822159d8fb9278920899892df7aec916c2c6002011d52a25d2cbee4d76f5a4e7531e622f8902406264dbe6b8e255564a57694732847f494261210488f5c95cc1c1ba98dedae138c09f4ba0d73c9454ad8cd682fcc007c0df727d646071630e4729143e528c607502406dc8a347c3cbfcdb4b63aaef75bdb461e90e064817fe18bd06d0895fcab7ee74f5ddfb9550c51c6e02433fdfd7f7b51ec8105908d94652270ed802b32f2f63790241009951931fa0d4df28338c2b5d978ff6761a27487704a2f08558666e0aa063604c3a2066009693a2bff04808c5eb381b1d775fb88fa9f579dc474861fdab8028ba
这便是原始的
ASN.1
格式内容,首先我们要知道ASN.1
是一种用来描述数据结构的抽象语法,它并不是只用来存储密钥,在ASN.1
中你可以自定义你的数据类型和数值约束等条件,你可以把它理解为一种序列化数据的格式。其中数据格式如下数据 长度 名称 含义 30 1 标记符 代表ASN.1结构的开始 82 1 长度类型 代表后面跟着一个双字节长度 025b 2 长度 代表后续内容的总长度为603字节 02 1 类型 代表整型 01 1 长度 代表1字节 00 1 值 代表整数0 02 1 类型 代表整型 81 1 长度类型 代表后面跟着一个单字节长度 80 1 长度 代表数据长度为128字节 … 128 值 数据值1 … 以此往复,所以除了最开始的标识符外,后续的内容都是由
类型
+长度
+值
构成的,而这些值根据他们出现的位置不同有着不同的含义,例如最开始出现的值0
在PEM中代表版本号信息。随后的数据值1代表模数。完整顺序如下
版本 模数 - n 加密指数 - e 解密指数 - d 素因子1 - p 素因子2 - q 指数1 - dp 指数2 - dq 系数 - invert(q, p) 其他额外信息
在了解了该格式后我们就能对上面的16进制数据直接分段了
标识头 30 总长度 82 025b 版本信息 0201 00 n 028180 70647879fa43aee8a5d3785754b7988d12b14b98cf39be21b5d90f3e3264e316cf99c3058b89480f2b46a0eda9bee6b4d753199671ef17bab4ec257aabe45e7eb6f668146597a0c9a05d0523e7511e8852a76eaa92e81d16afac7af522423aa00983110ce2d65099d37c748cc2aea0e2a9f4b0613fd8a30a8cbd89187db47f85 e 0203 010001 d 028180 2f2e7e44f682a352970a876261f610dc6814759fd89e6ceac9e42d39f6fdd337283f6c574f9479e3a44f2a0f9b4ac09efa25b0802fa4275a01c9809256c6afc4032ffd486f9e029681a3de126f607603559c7837fc17589d91750ebd4ff791dff06f0fe02d92f26bc582145e12519c74f534e832a4960c4248e24375ee953901 p 0241 009bfd4a17d26da30d2b7d4e920f4b488ef014babb8a2151979917bd228bb180771c028736557afd92f54d5da3837ef27fd322d9fd06c790155dbc22ff579ff51d q 0241 00b873912447c540c3c96a78f2f00a40898ac5ca0043aa3d914e8920822159d8fb9278920899892df7aec916c2c6002011d52a25d2cbee4d76f5a4e7531e622f89 dp 0240 6264dbe6b8e255564a57694732847f494261210488f5c95cc1c1ba98dedae138c09f4ba0d73c9454ad8cd682fcc007c0df727d646071630e4729143e528c6075 dq 0240 6dc8a347c3cbfcdb4b63aaef75bdb461e90e064817fe18bd06d0895fcab7ee74f5ddfb9550c51c6e02433fdfd7f7b51ec8105908d94652270ed802b32f2f6379 inv(q,p) 0241 009951931fa0d4df28338c2b5d978ff6761a27487704a2f08558666e0aa063604c3a2066009693a2bff04808c5eb381b1d775fb88fa9f579dc474861fdab8028ba
现在回到本题我们也先进行base64解码得到,因为这里是密钥的后半截,所以我们不能从前往后直接解析,需要先找到一个定位锚点,我们可以按照
02
先给数据初步分段3a1d7b10ba7f9f652b9ea83f81a15b2961e00cd63374f346a8d78f81dad1b697f3abe8eced0 02260d89f2d12bc9395c233012b6f744a380d1d0aed78c9fb52a28d95f858c630bd3b164e857b406f541311426d64d5f0b6e0d1 02818100befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803 02818077a554614c111b51dea903a8e8222be795b45948805fc6f5ea67e60ad493f117b3033ea2ee84d87c0a29a87ea38908a93e313e08fe83dc91ba8695ba969d40f243addff620ee40bda8562ff5389661efd8b9d5976bacf2bc9a1cfc54d7704c7098441b1e7253760fc7dbcef7a417082e7492e8e0808f34d830c772e800714f41 0281801c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b 028181008488efbea72e9abac7f71085b86e9071bece170f8c92a387aebdcc89f8915c33739609a73cacd5559b3a56fd59082ed311bab49f42ea0ba6be5e253453db0fc8b5b6aab458163b8a013121fd5c554dcc51d81c57e60f59d9d7f8f4d45fab24365da039ed8fb5401cfaff0c8aae2191ae8bd742351d11034ff0f0c32fb0586810
不一定数据中出现了
02
就代表是数据开始的地方,因为也有可能数据中也包含02,我们要一一检验02
后面的长度标识定义的长度时候符合后续数据的长度,我们会发现第一块后面的长度标识以及长度都不符合正常的定义,所以其实第一块这里的02是上一块数据中的内容而非新数据的开始标识。最终我们可以得到3a1d7b10ba7f9f652b9ea83f81a15b2961e00cd63374f346a8d78f81dad1b697f3abe8eced002260d89f2d12bc9395c233012b6f744a380d1d0aed78c9fb52a28d95f858c630bd3b164e857b406f541311426d64d5f0b6e0d1 q 028181 00befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803 dp 028180 77a554614c111b51dea903a8e8222be795b45948805fc6f5ea67e60ad493f117b3033ea2ee84d87c0a29a87ea38908a93e313e08fe83dc91ba8695ba969d40f243addff620ee40bda8562ff5389661efd8b9d5976bacf2bc9a1cfc54d7704c7098441b1e7253760fc7dbcef7a417082e7492e8e0808f34d830c772e800714f41 dq 028180 1c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b inv(q,p) 028181 008488efbea72e9abac7f71085b86e9071bece170f8c92a387aebdcc89f8915c33739609a73cacd5559b3a56fd59082ed311bab49f42ea0ba6be5e253453db0fc8b5b6aab458163b8a013121fd5c554dcc51d81c57e60f59d9d7f8f4d45fab24365da039ed8fb5401cfaff0c8aae2191ae8bd742351d11034ff0f0c32fb0586810
我们发现有用的数据为
q,dp,dq,inv(q,p)
from Crypto.Util.number import *
q = 0x00befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803
dq = 0x1c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b
c = 2329206064672111950904450292941421573350591294207157652026787098178545948258554492347649016030892000747909819064473414536692222493030122267884839986067073054508582403564557167583565364976046083954888777809177108315052118912603290095925912298584322873410379937455462434313487981715516761071523410121549134193124709612876311518391130974466069686830456036397449773159386026998482557500868323733155606973727191287617806211911722356975478414165867941665666556476756617951672736466672410799762479373101996896644454778482896784598378016390592459460753042458284030795009957030383305268628413551730442224404807955926606496353
m = pow(c, dq, q)
print(long_to_bytes(m))
## openssl 格式
直接输入`openssl help`你会发现打印出来了命令列表
Standard commands
asn1parse ca ciphers cms
crl crl2pkcs7 dgst dhparam
dsa dsaparam ec ecparam
enc engine errstr gendsa
genpkey genrsa help list
nseq ocsp passwd pkcs12
pkcs7 pkcs8 pkey pkeyparam
pkeyutl prime rand rehash
req rsa rsautl s_client
s_server s_time sess_id smime
speed spkac srp storeutl
cast-cbc cast5-cbc cast5-cfb cast5-ecb
cast5-ofb des des-cbc des-cfb
des-ecb des-ede des-ede-cbc des-ede-cfb
des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb
des-ede3-ofb des-ofb des3 desx
idea idea-cbc idea-cfb idea-ecb
idea-ofb rc2 rc2-40-cbc rc2-64-cbc
rc2-cbc rc2-cfb rc2-ecb rc2-ofb
rc4 rc4-40 seed seed-cbc
seed-cfb seed-ecb seed-ofb sm4-cbc
sm4-cfb sm4-ctr sm4-ecb sm4-ofb
这里的每一项都代表特定的算法或者证书格式,而我们这里涉及的是`rsa`,所以我们使用`openssl rsa -help`进一步查看帮助文档
Usage: rsa [options]
Valid options are:
-help Display this summary 查看帮助
-inform format Input format, one of DER PEM 指定输入格式
-outform format Output format, one of DER PEM PVK 指定输出格式
-in val Input file 输入文件
-out outfile Output file 输出文件
-pubin Expect a public key in input file 期望输入时一个公钥
-pubout Output a public key 输出公钥文件
-passout val Output file pass phrase source 给输出文件设置密码
-passin val Input file pass phrase source 输入文件的密码
-RSAPublicKey_in Input is an RSAPublicKey 指定输入是一个RSA公钥
-RSAPublicKey_out Output is an RSAPublicKey 指定输出是一个RSA公钥
-noout Don't print key out 不要打印输入的密钥内容
-text Print the key in text 用文本格式打印密钥内容
-modulus Print the RSA key modulus 打印RSA密钥的模数
-check Verify key consistency 检测密钥是否一致
-* Any supported cipher 其他参数
-pvk-strong Enable 'Strong' PVK encoding level (default) 启用“强”PVK编码级别(默认)
-pvk-weak Enable 'Weak' PVK encoding level 启用“弱”PVK编码级别
-pvk-none Don't enforce PVK encoding 不强制执行PVK编码
-engine val Use engine, possibly a hardware device 指定解析引擎
这里列出了和`rsa`证书有关的项,后面中文内容是我额外添加的释义,那么我们要查看密钥内容的话直接
openssl rsa -in key.pem -inform PEM
会发现他将密钥内容又打印了一遍,这是因为它默认就是只做解析不会有其他操作,我们需要具体指定我们想要的操作,例如`-text`以文本内容输出参数,
openssl rsa -in key.pem -inform PEM -text
此时我们得到输出
RSA Private-Key: (1023 bit, 2 primes)
modulus:
70:64:78:79:fa:43:ae:e8:a5:d3:78:57:54:b7:98:
8d:12:b1:4b:98:cf:39:be:21:b5:d9:0f:3e:32:64:
e3:16:cf:99:c3:05:8b:89:48:0f:2b:46:a0:ed:a9:
be:e6:b4:d7:53:19:96:71:ef:17:ba:b4:ec:25:7a:
ab:e4:5e:7e:b6:f6:68:14:65:97:a0:c9:a0:5d:05:
23:e7:51:1e:88:52:a7:6e:aa:92:e8:1d:16:af:ac:
7a:f5:22:42:3a:a0:09:83:11:0c:e2:d6:50:99:d3:
7c:74:8c:c2:ae:a0:e2:a9:f4:b0:61:3f:d8:a3:0a:
8c:bd:89:18:7d:b4:7f:85
publicExponent: 65537 (0x10001)
privateExponent:
2f:2e:7e:44:f6:82:a3:52:97:0a:87:62:61:f6:10:
dc:68:14:75:9f:d8:9e:6c:ea:c9:e4:2d:39:f6:fd:
d3:37:28:3f:6c:57:4f:94:79:e3:a4:4f:2a:0f:9b:
4a:c0:9e:fa:25:b0:80:2f:a4:27:5a:01:c9:80:92:
56:c6:af:c4:03:2f:fd:48:6f:9e:02:96:81:a3