我正在开发一款 CTF 游戏:
使用 ECB 模式的 AES 加密。所有值均采用 base64 编码
ciphertext = 8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L
key = 3q1FxGhuZ5fQYbjzDxgQ35==
我尝试在终端中解密,将密文保留为 base64 格式,并使用标志-base64
,但没有成功。然后我去了http://extranet.cryptomathic.com/aescalc,将值转换为十六进制后,我就能解密:
key: DEAD45C4686E6797D061B8F30F1810DF
text: F0B0545597C37C8EB09E0806D6E518B90B11A06774F291B01C237EF91E6B69B316F4F26658759C4AB8F2E537DF7E3E8B
out: 7B796F755F73686F756C645F6E6F745F706F73745F7468655F61637475616C5F6374665F76616C75657D5F5F5F5F5F5F
然后我回到我的终端尝试:
echo -n F0B0545597C37C8EB09E0806D6E518B90B11A06774F291B01C237EF91E6B69B316F4F26658759C4AB8F2E537DF7E3E8B | openssl enc -d -K DEAD45C4686E6797D061B8F30F1810DF -aes-128-ecb -nosalt
但我得到了同样的错误:
bad decrypt
140735124906848:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:531:
我已在 Ubuntu 17.04 上尝试过此操作,现在在 MacOSX 上使用 OpenSSL 1.0.2l 尝试过。为什么我无法在自己的终端中解密?
答案1
因为openssl
默认使用 PKCS#7 填充,而您的纯文本不包含 PKCS#7 填充。如果您的纯文本已填充,则已用值 的字节填充5F
。请改用选项-nopad
,用值填充5F
不是我所知的任何填充方案;如果需要删除,您需要自己删除它。
您当前以十六进制显示输入。十六进制是字节的表示形式,而不是字节值本身。您需要使用文件的 直接从文件中输入源材料, <
或者通过十六进制解码输入。
输出也将是二进制的;它不会表示任何可读的纯文本。因此,您可能需要先将输出转换为十六进制,然后再将其与问题中的值进行比较。
答案2
好吧,您可能需要考虑使用 python 或任何其他脚本/编程语言来做这样的事情。
以编程方式执行此操作的优点是:
- 无论何时您需要重做类似的事情,您都可以准备好代码。
- 您可以写注释来解释发生了什么,所以如果您需要了解之前所做的事情,希望代码加上注释能够帮助您做到这一点。
- 编码、字节处理等许多事情都比在控制台中更容易
- 大多数语言都是跨平台的,因此只要你切换设备,它就可以轻松地在 Windows、Linux 和 Android 上运行。
关于你手头的问题,可以使用 python 来解决:
# we import the function we need from common librairies
from base64 import b64decode
from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
# First we decode the message and the key from base64 into bytes:
msg = b64decode("8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L")
key = b64decode("3q1FxGhuZ5fQYbjzDxgQ35==")
# We then instantiate a cipher_suite using AES with the provided key, in ECB mode
cipher_suite = AES.new(key, AES.MODE_ECB)
# We can decrypt the message using our cipher_suite:
recovered = cipher_suite.decrypt(msg)
# We can print it:
print ("plaintext: ", recovered)
# There is some garbage at the end, but if we display it in hexadecimal form:
print ("in hex:", hexlify(recovered))
# We can see it's just padding using '5f', so let's create a function to remove such padding:
def unpad(padded):
# we declare the value of our padding:
paddingByte = unhexlify('5f')
# we do a loop, while the last byte is padding
while padded[-1:]==paddingByte:
# we remove the last byte
padded = padded[:-1]
# once it's done, we return
return padded
# We can now use our function to remove padding:
print ("unpadded: ", unpad(recovered))
现在,如果你不想学习 Python 或任何其他语言,并且/或者如果你真的如果你想在你的终端上完成所有操作,这也是可能的:那么你可以直接使用管道将数据从一个命令传递到另一个命令,命令替换将正确的密钥输入到 openssl,然后命令base64
处理 base64 plusxxd
将二进制数据转换为十六进制(用于 openssl 中的密钥),最后使用sed
删除5f
填充:
echo "8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L" | base64 --decode | openssl enc -d -K $(echo "3q1FxGhuZ5fQYbjzDxgQ35==" | base64 --decode | xxd -c 16 -ps) -aes-128-ecb -nosalt -nopad | sed 's/_*$//g'
我不知道为什么,但我个人发现 Python 方法更简洁。
您还提到,按照 Maarten Bodewes 指出的方法,您得到的是垃圾,这是因为您向 OpenSSL 输入了十六进制值,而您应该提供直接二进制数据(不是十六进制值)作为消息,而您应该提供十六进制的密钥:
echo -n f0b0545597c37c8eb09e0806d6e518b90b11a06774f291b01c237ef91e6b69b316f4f26658759c4ab8f2e537df7e3e8b | xxd -r -p | openssl ...
附言:你应该避免发布你在 CTF 中遇到的实际值,因为这可能会破坏那些第一反应是去谷歌搜索这些值的人的游戏体验。
答案3
通过 base64 进行解码
echo 8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L | base64 -D > aesdata.dat
(这-D
是 mac OS 的一个怪癖。Linux 倾向于使用-d
或--decode
代替)。
相似地:
echo 3q1FxGhuZ5fQYbjzDxgQ35== | base64 -D > aeskey.dat
但是 openssl 要求参数为十六进制值(但密码文件中为二进制值):
xxd -p < aeskey.dat
给出dead45c4686e6797d061b8f30f1810df
。或者,如果您想避免混乱,可以从上一个命令中传输。
最后:
openssl enc -d -nopad -aes-128-ecb -K dead45c4686e6797d061b8f30f1810df -in aesdata.dat -out plain
将会解密。-nopad 可避免解密错误,因为使用了非标准填充。
现在hd plain
检查结果,这确实是您所寻找的。