Python Code:
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
def encrypt_string(input_string, key_base64, str_iv):
try:
key = key_base64.encode('utf-8')
if str_iv:
iv = base64.b64decode(str_iv)
else:
iv = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_data = pad(input_string.encode(), AES.block_size)
cipher_data = cipher.encrypt(padded_data)
combined_data = iv + cipher_data
return base64.b64encode(combined_data).decode()
except Exception as e:
print(e)
if __name__ == "__main__":
key = "1bd393e7a457f9023d9ba95fffb5a2e1"
iv = "1oTOhV9xGyu1mppmWZWa5w=="
input_string = "AAAAAAA"
encrypted_data = encrypt_string(input_string, key, iv)
print("Encrypted string:", encrypted_data)`
OUTPUT :
Encrypted string: 1oTOhV9xGyu1mppmWZWa5+kzveiTRzRH+gRVHx+7Ad0=
PHP code:
<?php
function encrypt_string($input_string, $key_base64, $str_iv) {
try {
$key = base64_decode($key_base64);
if ($str_iv) {
$iv = base64_decode($str_iv);
} else {
$iv = openssl_random_pseudo_bytes(16);
}
$ciphertext = openssl_encrypt($input_string, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
$combined_data = $iv . $ciphertext;
return base64_encode($combined_data);
} catch (Exception $e) {
echo $e->getMessage();
}
}
$key = "1bd393e7a457f9023d9ba95fffb5a2e1";
$iv = "1oTOhV9xGyu1mppmWZWa5w==";
$input_string = "AAAAAAA";
$encrypted_data = encrypt_string($input_string, $key, $iv);
echo "Encrypted string: " . $encrypted_data . "n";
?>
OUTPUT:
Encrypted string: 1oTOhV9xGyu1mppmWZWa53Nc8rxWTultBWLvWitUICQ=
Please, does anyone know how to make the output of these two codes the same?
2
Answers
The issue is in your PHP code. The
openssl_encrypt
function takes apasspharse
argument:For AES 256, this should be 32 characters. Your
$key
variable is getting padded withnull
bytes.The difference is in the first line of each function.
In your Python code, this just converts the ASCII key from a string to bytes. Since the length of the initial key is 32, this is fine.
In your PHP, this decodes the from base 64 back to bytes, which shortens the key to 24 bytes.
To make the Python code the same as the PHP code, you would need to do:
However, this will fail later on in the code in Python because the length of the key is only 24, and Python does not silently pad the key.
One work around is to use a hash of the key for the passphrase and truncate it to 32 characters. Keep in mind this is NOT a good security practice, see @maarten-bodewes comment below. Here are examples in Python and PHP. I removed the exception handling for simplicity.
And the PHP code:
If you want to get the same result then just remove the
$key = base64_decode($key_base64)
. I did get the right result for$key = $key_base64
.Note that using the base64 encoding directly as key means that a maximum of 6 bits contain random information for each character / byte of input. So if you insert 32 characters then the key size is effectively 32 * 6 = 192 bits. That’s more than enough, but clearly less than the 256 bits "promised" by AES-256.
The other issue is that CBC doesn’t offer integrity / authenticity.
I’d recommend never to convert keys to strings in the first place – just keep them binary. Furthermore, if you’re going to encrypt with a symmetric key then use something like NaCL or another library that performs higher level "sealing" rather than just encryption. If you want to use a password then you should use a good Password Based Key Derivation Function (PBKDF) such as PBKDF2 or Argon2.