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:
- Generate 8 bytes of random data as salt.
- Derive AES key and IV from password using the salt from step 1.
- Pad the input data with PKCS#7.
- Encrypt the padded using AES-256 in CBC mode with the key and the IV from step 2.
- Encode in Base64 and output the salt from step 1.
- Encode in Base64 and output the encrypted data from step 4.
The steps from decrypting are the reverse:
- Decode the input data from Base64 into a binary string.
- Treat the first 8 bytes of the decoded data as salt.
- Derive AES key and IV from password using the salt from step 1.
- Decrypt the remaining decoded data using the AES key and the IV from step 3.
- Verify and remove the PKCS#7 padding from the result.