从 OpenSSL AES 解密 python 中的 AES CBC

Decrypting AES CBC in python from OpenSSL AES

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

这是我在 OpenSSL 中所做的

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

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

我试过了(显然不行)

obj2 = AES.new("mypassword", AES.MODE_CBC)
output = obj2.decrypt(text)

我只想做python中的第二步,但是看样例的时候:

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

obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
obj2.decrypt(ciphertext)

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

How to decrypt OpenSSL AES-encrypted files in Python?

但没有帮助。

谁能告诉我如何使用 python 来做到这一点?

谢谢。

OpenSSL 语句使用 PBKDF2 创建一个 32 字节的密钥和一个 16 字节的 IV。为此,隐式生成一个随机的 8 字节盐,并应用指定的密码、迭代计数和摘要(默认值:SHA-256)。 key/IV 对用于使用 CBC 模式的 AES-256 和 PKCS7 填充 s 对明文进行加密。 here。结果以OpenSSL格式返回,以Salted__[=39=的8字节ASCII编码开头,后面是8字节salt和实际密文,全部Base64编码。解密需要加盐,这样才能重构key和IV。

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

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

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

The quick brown fox jumps over the lazy dog

被加密成密文

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 

使用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)

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

在二进制存储密文的情况下:在解密过程中,第一步只应(二进制)读取第一个块(16 字节)。由此,可以确定盐(字节 8 到 16),然后是密钥和 IV(类似于上面发布的代码)。
密文的其余部分可以(二进制)以合适大小的块(=块大小的倍数,例如 1024 字节)读取。每个块都是 encrypted/decrypted 单独的,请参阅 multiple encrypt/decrypt-calls. For reading/writing files in chunks with Python see e.g. here
最好在单独的问题范围内回答更多详细信息。