The situation I\'m trying to solve: in my Cocoa app, I need to encrypt a string with a symmetric cipher, POST it to PHP, and have that script decode the data. The process n
I figured out my problem. The short answer: the key being used was of different lengths under Cocoa and PHP. The long answer...
My original inquiry was using Blowfish/CBC which is a variable key length cipher from 16 bytes to 56. Going off of Boaz's idea that the key was somehow to blame, I switched to TripleDES for the cipher as that uses a fixed key length of 24 bytes. It was then I noticed a problem: the key returned by Cocoa/EVP_BytesToKey() was 24 bytes in length, but the value returned by md5() hashing my key was only 16.
The solution to the problem was to have PHP create a key the same way EVP_BytesToKey
does until the output length was at least (cipherKeyLength + cipherIVLength). The following PHP does just that (ignoring any salt or count iterations)
$cipher = MCRYPT_TRIPLEDES;
$cipherMode = MCRYPT_MODE_CBC;
$keySize = mcrypt_get_key_size( $cipher, $cipherMode );
$ivSize = mcrypt_get_iv_size( $cipher, $cipherMode );
$rawKey = "ThisIsMyKey";
$genKeyData = '';
do
{
$genKeyData = $genKeyData.md5( $genKeyData.$rawKey, true );
} while( strlen( $genKeyData ) < ($keySize + $ivSize) );
$generatedKey = substr( $genKeyData, 0, $keySize );
$generatedIV = substr( $genKeyData, $keySize, $ivSize );
$output = mcrypt_decrypt( $cipher, $generatedKey, $encodedData, $cipherMode, $generatedIV );
echo "output (hex)" . bin2hex($output);
Note that there'll most likely be PKCS#5 padding on the end of that output. Check out the comments here http://us3.php.net/manual/en/ref.mcrypt.php for pkcs5_pad
and pkcs5_unpad
for adding and removing said padding.
Basically, take the raw md5 value of the key and if that isn't long enough, append the key to the md5 result and md5 that string again. Wash, rinse, repeat. The man page for EVP_BytesToKey() explains what it's actually doing and shows where one would put salt values, if needed. This method of regenerating the key also correctly regenerates the initialization vector (iv) so it's not necessary to pass it along.
But what about Blowfish?
EVP_BytesToKey()
returns the smallest key possible for a cipher as it doesn't accept a context by which to base a key size from. So the default size is all you get, which for Blowfish is 16 bytes. mcrypt_get_key_size()
, on the other hand, returns the largest possible key size. So the following lines in my original code:
$keySize = mcrypt_enc_get_key_size( $td );
$key = substr( md5( "ThisIsMyKey" ), 0, $keySize );
would always return a 32 character key because $keySize is set to 56. Changing the code above to:
$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;
$keySize = 16;
allows blowfish to decode properly but pretty much ruins the benefit of a variable length key. To sum up, EVP_BytesToKey()
is broken when it comes to variable key length ciphers. You need to create a key/iv differently when using a variable key cipher. I didn't go into it much because 3DES will work for what I need.