从 OpenSSL AES 在 python 中解密 AES CBC

gmm*_*mmo 6 python encryption openssl pycrypto pycryptodome

我需要使用 python 解密在 OpenSSL 上加密的文件,但我不了解 pycrypto 的选项。

这是我在 OpenSSL 中所做的

  1. openssl enc -aes-256-cbc -a -salt -pbkdf2 -iter 100000 -in "clear.txt" -out "crypt.txt" -pass pass:"mypassword"

  2. openssl enc -d -aes-256-cbc -a -pbkdf2 -iter 100000 -in "crypt.txt" -out "out.txt" -pass pass:"mypassword"

我试过了(这显然行不通)

obj2 = AES.new("mypassword", AES.MODE_CBC)
output = obj2.decrypt(text)
Run Code Online (Sandbox Code Playgroud)

我只想在python中做第二步,但是在查看示例时:

https://pypi.org/project/pycrypto/

obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
obj2.decrypt(ciphertext)
Run Code Online (Sandbox Code Playgroud)

我不需要IV,我如何指定盐?pbkdf2 哈希?我也在看这个线程

如何在 Python 中解密 OpenSSL AES 加密的文件?

但没有帮助。

有人可以告诉我如何使用 python 做到这一点吗?

谢谢你。

Top*_*aco 9

OpenSSL 语句使用 PBKDF2 创建 32 字节密钥和 16 字节 IV。为此,会隐式生成随机 8 字节盐,并应用指定的密码、迭代计数和摘要(默认值:SHA-256)。密钥/IV 对用于使用 CBC 模式下的 AES-256 和 PKCS7 填充来加密明文。这里。结果以 OpenSSL 格式返回,以Salted__的 8 字节 ASCII 编码开头,后跟 8 字节 salt 和实际密文,全部采用 Base64 编码。解密需要盐,以便可以重建密钥和IV。

注意,OpenSSL语句中的密码实际上是不带引号传递的,即在发布的OpenSSL语句中,引号是密码的一部分。

对于 Python 中的解密,必须首先从加密数据中确定盐和实际密文。使用盐可以重建密钥/IV 对。最后,密钥/IV 对可用于解密。

示例:使用发布的 OpenSSL 语句,明文

The quick brown fox jumps over the lazy dog
Run Code Online (Sandbox Code Playgroud)

被加密成密文

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 
Run Code Online (Sandbox Code Playgroud)

使用 Python 解密如下(使用PyCryptodome):

from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES
import base64

# Determine salt and ciphertext
encryptedDataB64 = 'U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw=='
encryptedData = base64.b64decode(encryptedDataB64)
salt = encryptedData[8:16]
ciphertext = encryptedData[16:]

# Reconstruct Key/IV-pair
pbkdf2Hash = PBKDF2(b'"mypassword"', salt, 32 + 16, count=100000, hmac_hash_module=SHA256)
key = pbkdf2Hash[0:32]
iv = pbkdf2Hash[32:32 + 16]

# Decrypt with AES-256 / CBC / PKCS7 Padding
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(ciphertext), 16)

print(decrypted)
Run Code Online (Sandbox Code Playgroud)

编辑- 关于您的评论:16 MB 应该是可能的,但对于更大的数据,通常会从文件中读取密文,并将解密的数据写入文件中,与上面发布的示例相反。
数据能否一步解密最终取决于可用内存。如果内存不够,就必须分块处理数据。
当使用块时,不使用 Base64 编码加密数据而是直接以二进制格式存储它们会更有意义。这可以通过在 OpenSSL 语句中省略-a选项来实现。否则,必须确保始终加载块大小的整数倍(相对于未解码的密文),其中未解码的密文的 3 个字节对应于 Base64 编码的密文的 4 个字节。

对于二进制存储的密文:在解密过程中,第一步中仅应读取第一个块(16 字节)(二进制)。由此,可以确定盐(字节 8 到 16),然后确定密钥和 IV(类似于上面发布的代码)。
密文的其余部分可以以适当大小的块(= 块大小的倍数,例如 1024 字节)的形式(二进制)读取。每个块都是单独加密/解密的,请参阅多个加密/解密调用。有关使用 Python 读取/写入块文件的信息,请参见此处
更多详细信息最好在单独的问题范围内得到回答。