Those 2 codes are working separately but do not give the same result even if they rely on the same library
They both :
PHP
<?php
$encrypt_method = "AES-256-CBC";
$secret_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$secret_iv = 'YYYYYYYYY';
// hashes
$key = substr(hash('sha256', $secret_key), 0, 32);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$string = file_get_contents('plaintext.txt');
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
//By default the output is already in base64
file_put_contents('ciphertext.base64', $output);
?>
BASH
key='XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
secret_iv='YYYYYYYYY'
keyhash=$(echo -n $key | sha256sum | cut -c 1-32)
ivhash=$(echo -n $secret_iv | sha256sum | cut -c 1-16)
openssl enc -aes-256-cbc -e -K "$keyhash" -iv "$ivhash" -in plaintext.txt -out ciphertext.base64 -base64
I really don't understand why the results are different. Moreover in php the IV must have a length of 16 while the shells complains it should be 32
Any tip ?
In bash, I've noticed 32 0 are added to key (64 characters) and 16 0 to IV (32 characters) which is probably the result of the warning "hex string is too short, padding with zero bytes to length"
While the key and IV in PHP/OpenSSL must be passed as raw values (i.e. binary strings) to openssl_encrypt()
, they must be specified hex encoded in the OpenSSL statement (s. openssl-enc, -K
. -iv
options).
Both are not done correctly in the code (possibly because, as already suspected in the comment, binary and hex encoded data are confused):
In the PHP code, for the key and IV not the raw hash values are used, but the hex encoded hash values. The direct use of hex encoded hash values is a vulnerability, as the value range for each byte is reduced from 256 to 16 bytes. This should therefore be fixed. To do this, the third parameter of the hash()
function must be set to true
:
$key = hash('sha256', $secret_key, true);
$iv = substr(hash('sha256', $secret_iv, true), 0, 16);
In the bash script, the hex encoded hash values are truncated. To fix this, use the correct lengths (64 hex digits or 32 bytes for the key and 32 hex digits or 16 bytes for the IV):
keyhash=$(echo -n $key | sha256sum)
ivhash=$(echo -n $secret_iv | sha256sum | cut -c 1-32)
If the PHP code cannot/should not be changed despite the mentioned disadvantage and the hex encoded values are to be used directly as key and IV, an additional hex encoding is required in the batch script:
keyhashstr=$(echo -n $key | sha256sum | cut -c 1-32)
keyhash=$(printf '%s' $keyhashstr | hexdump -ve '/1 "%02X"')
ivhashstr=$(echo -n $secret_iv | sha256sum | cut -c 1-16)
ivhash=$(printf '%s' $ivhashstr | hexdump -ve '/1 "%02X"')
(or with xxd
instead of hexdump
).
Security: Regarding the term secret_iv
: The IV is not a secret and therefore does not need to be kept secret. However, it is a vulnerability if a static IV is used, as this results in a reuse of key/IV pairs if the same key is used. To prevent this, a common approach is to generate a random IV for each encryption. The IV is passed together with the ciphertext to the decrypting side, usually concatenated.
When using a passphrase, another vulnerability is to use a fast digest for key derivation. More secure is a key derivation function such as PBKDF2 in conjunction with a random (also not secret) salt.