I'm trying to implement the following in Python: openssl enc -e -aes-256-cbc -base64 -k "Secret Passphrase" -in plaintext.txt -out ciphertext.txt
openssl enc -d -aes-256-cbc -base64 -k "Secret Passphrase" -in ciphertext.txt -out verification.txt
I've tried several different modules, PyCrypto, M2Crypto, etc but can't seem to get the correct combination of changing the password to the right size key and encoding everything correctly. I've found https://github.com/nvie/SimpleAES but that basically runs OpenSSL on the command line, which I'd rather avoid.
Base 64 encoding and decoding can be easily handled via the standard base64
module.
AES-256 decryption and encryption in CBC mode are supported by both PyCrypto and M2Crypto.
The only non-standard (and most difficult) part is the derivation of the IV and the key from the password. OpenSSL does it via its own EVP_BytesToKey
function, which is described in this man page.
The Python equivalent is:
def EVP_BytesToKey(password, salt, key_len, iv_len):
"""
Derive the key and the IV from the given password and salt.
"""
from hashlib import md5
dtot = md5(password + salt).digest()
d = [ dtot ]
while len(dtot)<(iv_len+key_len):
d.append( md5(d[-1] + password + salt).digest() )
dtot += d[-1]
return dtot[:key_len], dtot[key_len:key_len+iv_len]
where key_len
is 32 and iv_len
is 16 for AES-256. The function returns the key and the IV which you can use to decrypt the payload.
OpenSSL puts and expects the salt in the first 8 bytes of the encrypted payload.
Finally, AES in CBC mode can only work with data aligned to the 16 byte boundary. The default padding used is PKCS#7.
The steps for encrypting are therefore:
The steps from decrypting are the reverse: