Расшифровка AES CBC в Python из OpenSSL AES

Sep 30 2020

Мне нужно расшифровать файл, зашифрованный на OpenSSL, с помощью python, но я не понимаю возможности 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, как мне указать соль? хеш pbkdf2? Я тоже смотрел эту ветку

Как расшифровать файлы с шифрованием OpenSSL AES в Python?

но не помогло.

Может ли кто-нибудь показать мне, как это сделать с помощью Python?

Спасибо.

Ответы

4 Topaco Sep 30 2020 at 04:16

Оператор OpenSSL использует PBKDF2 для создания 32-байтового ключа и 16-байтового IV. Для этого неявно генерируется случайная 8-байтовая соль и применяется указанный пароль, количество итераций и дайджест (по умолчанию: SHA-256). Пара ключ / IV используется для шифрования открытого текста с помощью AES-256 в режиме CBC и заполнения PKCS7, s. здесь . Результат возвращается в формате 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 МБ должно быть возможно, но для больших данных зашифрованный текст обычно будет считываться из файла, а дешифрованные данные будут записаны в файл, в отличие от примера, опубликованного выше.
Возможность дешифрования данных за один шаг в конечном итоге зависит от доступной памяти. Если памяти недостаточно, данные должны обрабатываться порциями.
При использовании кусков было бы разумнее не кодировать зашифрованные данные Base64, а хранить их непосредственно в двоичном формате. Это возможно, если опустить параметр -a в операторе OpenSSL. В противном случае необходимо гарантировать, что всегда загружаются целые числа, кратные размеру блока (относительно недекодированного зашифрованного текста), где 3 байта незакодированного зашифрованного текста соответствуют 4 байтам зашифрованного текста, закодированного в Base64.

В случае двоичного сохраненного зашифрованного текста: во время дешифрования только первый блок (16 байтов) должен быть (двоичным) считан на первом этапе. Исходя из этого, можно определить соль (байты с 8 по 16), затем ключ и IV (аналогично опубликованному выше коду).
Остальная часть зашифрованного текста может быть (двоична) считана кусками подходящего размера (= кратному размеру блока, например 1024 байта). Каждый фрагмент шифруется / дешифруется отдельно, см. Несколько вызовов шифрования / дешифрования . Для чтения / записи файлов кусками с помощью Python см., Например, здесь .
На дополнительные подробности лучше всего ответить в рамках отдельного вопроса.