Using openssl for AES-CBC-PKCS5Padding rather than mcrypt in PHP
Yeah, that’s quite an acronym soup.
Background: the mcrypt library for PHP has been deprecated for a long time now. However, in PHP we still have to process lots encrypted strings coming from a format like MCRYPT_RIJNDAEL_128 or stuff coming from Java (Android, I’m looking at you!), that was encrypted with a AES with Cipher Blocker Chaining and PKCS5Padding. These cipher algorithms are not explicitly included in openssl as such, although you can find stray references all over the web pointing you in the general direction.
I frequently have to integrate with third-party sites or services that are written in Java, and which provide sample PHP code for implementing my end. Because the ciphers in mcrypt are easier to identify, this provided source usually uses the deprecated library rather than openssl.
So, to save some time, here’s the equivalent openssl encryption/decryption commands:
openssl_encrypt($plaintext,'aes-128-cbc',$key,0,$iv)
openssl_decrypt($encrypted, 'aes-128-cbc',$key, 0, $iv)
For a more verbose proof-of-concept, a longer test program is included below. But before you look at that, consider the following warnings:
DO NOT USE A FIXED INITIALIZATION VECTOR!
DO NOT USE STUPID PASSWORDS!
DO NOT USE THIS CODE IN PRODUCTION!
<?php
// sooper-secret message
$src = array('don' => 'sleeper agent', 'mike' => 'coverup');
// ultra-seekrit key
$key = '1234567890123456';
// hard-coded initialization vector to prove we really know our stuff
$iv = '6543210987654321';
$original = json_encode($src);
// encrypt with mcrypt
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $size - (strlen($original) % $size);
$plain = $original . str_repeat(chr($pad), $pad);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($module, $key, $iv);
$data = mcrypt_generic($module, $plain);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
$mcrypted = base64_encode($data);
// encrypt with openssl
$ocrypted = openssl_encrypt($original, 'aes-128-cbc', $key, 0, $iv);
if (strcmp($mcrypted, $ocrypted))
{
echo "Uh-oh. Encrypted strings don't match up.\n";
echo "mcrypt encrypted string:\n$mcrypted\n";
echo "openssl encrypted string:\n$ocrypted\n";
}
else
{
// decrypt using mcrypt
$m_from_o_decrypt_padded = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($ocrypted), MCRYPT_MODE_CBC, $iv);
$dec_s = strlen($m_from_o_decrypt_padded);
$padding = ord($m_from_o_decrypt_padded[$dec_s - 1]);
$m_from_o_decrypt = substr($m_from_o_decrypt_padded, 0, -$padding);
// decrypt using openssl
$o_from_m_decrypt = openssl_decrypt($mcrypted, 'aes-128-cbc', $key, 0, $iv);
if (strcmp($o_from_m_decrypt, $m_from_o_decrypt))
{
echo "Uh-oh. Decrypted JSON strings don't match up.\n";
echo "openssl decrypting mcrypt encrypted string:\n$o_from_m_decrypt\n";
echo "mcrypt decrypting openssl encrypted string:\n$m_from_o_decrypt\n";
}
else
{
echo "Encrypted strings were the same, and each library decrypted the other's encrypted data\n.";
}
}