Odszyfrowanie AES CBC w Pythonie z OpenSSL AES

Sep 30 2020

Muszę odszyfrować plik zaszyfrowany w OpenSSL za pomocą Pythona, ale nie rozumiem opcji pycrypto.

Oto co robię w OpenSSL

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

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

Próbowałem (co oczywiście nie zadziała)

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

Chcę tylko zrobić drugi krok w Pythonie, ale patrząc na przykład:

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

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

Nie potrzebuję IV, jak określić sól? hash pbkdf2? Ja też patrzyłem na ten wątek

Jak odszyfrować pliki zaszyfrowane OpenSSL AES w Pythonie?

ale nie pomogło.

Czy ktoś może mi pokazać, jak to zrobić za pomocą Pythona?

Dziękuję Ci.

Odpowiedzi

4 Topaco Sep 30 2020 at 04:16

Instrukcja OpenSSL używa PBKDF2 do utworzenia 32-bajtowego klucza i 16-bajtowego IV. W tym celu losowo generowana jest 8-bajtowa sól i stosowane jest określone hasło, liczba iteracji i skrót (domyślnie: SHA-256). Para klucz / IV służy do szyfrowania tekstu jawnego za pomocą AES-256 w trybie CBC i wypełnienia PKCS7, s. tutaj . Wynik jest zwracany w formacie OpenSSL, który zaczyna się od 8- bajtowego kodowania ASCII Salted__ , po którym następuje 8- bajtowa sól i rzeczywisty zaszyfrowany tekst , wszystkie zakodowane algorytmem Base64. Sól jest potrzebna do odszyfrowania, aby można było odtworzyć klucz i IV.

Zauważ, że hasło w instrukcji OpenSSL jest w rzeczywistości przekazywane bez cudzysłowów, tj. W opublikowanej instrukcji OpenSSL znaki cudzysłowu są częścią hasła.

Do deszyfrowania w Pythonie należy najpierw określić sól i rzeczywisty zaszyfrowany tekst na podstawie zaszyfrowanych danych. Za pomocą soli można zrekonstruować parę klucz / kroplówka. Wreszcie, do odszyfrowania można użyć pary klucz / IV.

Przykład: w opublikowanej instrukcji OpenSSL tekst jawny

The quick brown fox jumps over the lazy dog

został zaszyfrowany w zaszyfrowanym tekście

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 

Deszyfrowanie za pomocą Pythona jest możliwe w następujący sposób (przy użyciu 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)

Edycja - jeśli chodzi o komentarz: 16 MB powinno być możliwe, ale w przypadku większych danych szyfrogram byłby generalnie odczytywany z pliku, a odszyfrowane dane byłyby zapisywane w pliku, w przeciwieństwie do przykładu zamieszczonego powyżej.
To, czy dane można odszyfrować w jednym kroku, ostatecznie zależy od dostępnej pamięci. Jeśli pamięć nie jest wystarczająca, dane muszą być przetwarzane w kawałkach.
Korzystając z fragmentów, bardziej sensowne byłoby nie kodowanie Base64 zaszyfrowanych danych, ale przechowywanie ich bezpośrednio w formacie binarnym. Jest to możliwe poprzez pominięcie opcji -a w instrukcji OpenSSL. W przeciwnym razie należy upewnić się, że ładowane są zawsze całkowite wielokrotności rozmiaru bloku (w stosunku do niekodowanego zaszyfrowanego tekstu), gdzie 3 bajty niezakodowanego zaszyfrowanego tekstu odpowiadają 4 bajtom zaszyfrowanego tekstu zaszyfrowanego Base64.

W przypadku binarnego tekstu zaszyfrowanego: Podczas deszyfrowania tylko pierwszy blok (16 bajtów) powinien zostać (binarnie) odczytany w pierwszym kroku. Na tej podstawie można określić sól (bajty od 8 do 16), następnie klucz i IV (analogicznie do zamieszczonego powyżej kodu).
Resztę zaszyfrowanego tekstu można odczytać (binarnie) w fragmentach o odpowiednim rozmiarze (= wielokrotność rozmiaru bloku, np. 1024 bajty). Każdy fragment jest szyfrowany / odszyfrowywany osobno, zobacz wiele wywołań szyfrowania / odszyfrowywania . Informacje o czytaniu / zapisywaniu plików w fragmentach w Pythonie można znaleźć np . Tutaj .
Na dalsze szczegóły najlepiej odpowiedzieć w ramach osobnego pytania.