使用Python从OpenSSL AES解密AES CBC

10

我需要使用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)

我只想在Python中完成第二步,但查看示例时:

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

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

我不需要IV,如何指定盐(salt)和pbkdf2哈希?我也看了这个线程

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

但没有帮助。

有人能向我展示如何使用Python做到这一点吗?

谢谢。

1个回答

9
OpenSSL语句使用PBKDF2生成32字节密钥和16字节IV。为此,隐式生成一个随机的8字节盐,并应用指定的密码、迭代次数和摘要(默认值:SHA-256)。使用密钥/IV对,在CBC模式和PKCS7填充的情况下使用AES-256加密纯文本,详情请见这里。结果以OpenSSL格式返回,该格式由8个ASCII编码的字节“Salted__”开始,后跟8个盐字节和实际的密文,所有内容均使用Base64编码。盐在解密过程中是必需的,以便可以重建密钥和IV。
请注意,OpenSSL语句中的密码实际上是没有引号的,即在发布的OpenSSL语句中,引号是密码的一部分。
在Python中进行解密时,必须先从加密数据中确定盐和实际的密文。使用盐可以重建密钥/IV对。最后,可以使用密钥/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字节)进行(二进制)读取。每个块都要单独加密/解密,请参见多重加/解密调用。有关使用Python以块读取/写入文件的详细信息,请参见此处。更多详细信息最好在单独问题的范围内回答。


谢谢。但是有一件事我没有解释清楚,我的加密数据相当大,它是一个16 MBytes文件。在你的解决方案中,我必须解密整个文件。我不确定Python是否能够处理它。因为它是块密码,我相信我们可以分块解密。你知道怎么做吗?假设你的encryptedDataB64是一个非常大的文件。谢谢! - gmmo
谢谢,我真的很感激你的解释和代码示例。关于这个主题的帖子并不是很好。 - gmmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接