Descriptografando AES CBC em python de OpenSSL AES

Sep 30 2020

Preciso descriptografar um arquivo criptografado em OpenSSL com python, mas não estou entendendo as opções de pycrypto.

Aqui está o que eu faço no 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"

Eu tentei (o que obviamente não funcionará)

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

Eu só quero fazer a segunda etapa em python, mas ao olhar para o exemplo:

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

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

Eu não preciso de IV, como especifico o sal? o hash pbkdf2? Eu também olhei para este tópico

Como descriptografar arquivos criptografados OpenSSL AES em Python?

mas não ajudou.

Alguém pode me mostrar como fazer isso usando python?

obrigado.

Respostas

4 Topaco Sep 30 2020 at 04:16

A instrução OpenSSL usa PBKDF2 para criar uma chave de 32 bytes e um IV de 16 bytes. Para isso, um salt aleatório de 8 bytes é gerado implicitamente e a senha especificada, a contagem de iteração e o resumo (padrão: SHA-256) são aplicados. O par chave / IV é usado para criptografar o texto simples com AES-256 no modo CBC e preenchimento PKCS7, s. aqui . O resultado é retornado no formato OpenSSL, que começa com a codificação ASCII de 8 bytes de Salted__ , seguido pelo sal de 8 bytes e o texto cifrado real, todos codificados em Base64. O sal é necessário para a descriptografia, para que a chave e o IV possam ser reconstruídos.

Observe que a senha na instrução OpenSSL é, na verdade, passada sem aspas, ou seja, na instrução OpenSSL postada, as aspas fazem parte da senha.

Para a descriptografia em Python, o salt e o texto cifrado real devem primeiro ser determinados a partir dos dados criptografados. Com o sal, o par chave / IV pode ser reconstruído. Finalmente, o par chave / IV pode ser usado para descriptografia.

Exemplo: com a instrução OpenSSL postada, o texto simples

The quick brown fox jumps over the lazy dog

foi criptografado no texto cifrado

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 

A descriptografia com Python é possível da seguinte forma (usando 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)

Editar - Com relação ao seu comentário: 16 MB deve ser possível, mas para dados maiores, o texto cifrado geralmente seria lido de um arquivo e os dados descriptografados seriam gravados em um arquivo, em contraste com o exemplo postado acima.
Se os dados podem ser descriptografados em uma etapa, em última análise, depende da memória disponível. Se a memória não for suficiente, os dados devem ser processados ​​em blocos.
Ao usar blocos, faria mais sentido não codificar os dados criptografados em Base64, mas armazená-los diretamente no formato binário. Isso é possível omitindo o -aopção na instrução OpenSSL. Caso contrário, deve-se garantir que sempre múltiplos inteiros do tamanho do bloco (em relação ao texto cifrado não codificado) sejam carregados, onde 3 bytes do texto cifrado não decodificado correspondem a 4 bytes do texto cifrado codificado em Base64.

No caso do texto cifrado armazenado binário: Durante a descriptografia, apenas o primeiro bloco (16 bytes) deve ser (binário) lido na primeira etapa. A partir disso, o sal pode ser determinado (os bytes 8 a 16), depois a chave e o IV (análogo ao código postado acima).
O resto do texto cifrado pode ser lido (binário) em pedaços de tamanho adequado (= um múltiplo do tamanho do bloco, por exemplo, 1024 bytes). Cada pedaço é criptografado / descriptografado separadamente, consulte várias chamadas para criptografar / descriptografar . Para ler / gravar arquivos em blocos com Python, veja, por exemplo, aqui .
Mais detalhes são respondidos melhor dentro do escopo de uma pergunta separada.