Décrypter AES CBC en python à partir d'OpenSSL AES

Sep 30 2020

J'ai besoin de décrypter un fichier crypté sur OpenSSL avec python mais je ne comprends pas les options de pycrypto.

Voici ce que je fais dans 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"

J'ai essayé (ce qui ne fonctionnera évidemment pas)

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

Je veux juste faire la deuxième étape en python, mais en regardant l'exemple:

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

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

Je n'ai pas besoin de IV, comment spécifier le sel? le hachage pbkdf2? Je suis aussi regardé ce fil

Comment décrypter les fichiers cryptés OpenSSL AES en Python?

mais n'a pas aidé.

Quelqu'un peut-il me montrer comment faire cela en utilisant python?

Merci.

Réponses

4 Topaco Sep 30 2020 at 04:16

L'instruction OpenSSL utilise PBKDF2 pour créer une clé de 32 octets et un IV de 16 octets. Pour cela, un sel aléatoire de 8 octets est généré implicitement et le mot de passe spécifié, le nombre d'itérations et le résumé (par défaut: SHA-256) sont appliqués. La paire clé / IV est utilisée pour crypter le texte en clair avec AES-256 en mode CBC et padding PKCS7, s. ici . Le résultat est renvoyé au format OpenSSL, qui commence par le codage ASCII de 8 octets de Salted__ , suivi du sel de 8 octets et du texte chiffré réel, tous codés en Base64. Le sel est nécessaire pour le décryptage, de sorte que la clé et l'IV peuvent être reconstruits.

Notez que le mot de passe dans l'instruction OpenSSL est en fait passé sans guillemets, c'est-à-dire que dans l'instruction OpenSSL publiée, les guillemets font partie du mot de passe.

Pour le décryptage en Python, le sel et le texte chiffré réel doivent d'abord être déterminés à partir des données chiffrées. Avec le sel, la paire clé / IV peut être reconstruite. Enfin, la paire clé / IV peut être utilisée pour le décryptage.

Exemple: avec l'instruction OpenSSL publiée, le texte en clair

The quick brown fox jumps over the lazy dog

a été chiffré dans le texte chiffré

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 

Le décryptage avec Python est possible comme suit (en utilisant 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)

Edit - Concernant votre commentaire: 16 Mo devraient être possibles, mais pour des données plus volumineuses, le texte chiffré serait généralement lu à partir d'un fichier et les données déchiffrées seraient écrites dans un fichier, contrairement à l'exemple posté ci-dessus.
Le déchiffrement des données en une seule étape dépend en fin de compte de la mémoire disponible. Si la mémoire n'est pas suffisante, les données doivent être traitées par blocs.
Lors de l'utilisation de blocs, il serait plus judicieux de ne pas encoder les données chiffrées en Base64 mais de les stocker directement au format binaire. Ceci est possible en omettant l' option -a dans l'instruction OpenSSL. Sinon, il faut s'assurer que toujours des multiples entiers de la taille du bloc (par rapport au texte chiffré non décodé) sont chargés, où 3 octets du texte chiffré non décodé correspondent à 4 octets du texte chiffré codé en Base64.

Dans le cas du texte chiffré stocké binaire: Pendant le décryptage, seul le premier bloc (16 octets) doit être lu (binaire) dans la première étape. À partir de là, le sel peut être déterminé (les octets 8 à 16), puis la clé et IV (analogue au code affiché ci-dessus).
Le reste du texte chiffré peut être lu (binaire) par blocs de taille appropriée (= un multiple de la taille du bloc, par exemple 1024 octets). Chaque morceau est chiffré / déchiffré séparément, voir plusieurs appels de chiffrement / déchiffrement . Pour lire / écrire des fichiers par blocs avec Python, voir par exemple ici .
Il est préférable de répondre à d'autres détails dans le cadre d'une question distincte.