I am trying to replicate the following CryptoJS 3.1.2 code with PHP 5.4.4 :
var plaintext = 'test';
var key = CryptoJS.enc.Utf8.parse('9rIY8vV8DdOKx3m6JocjEaObhhYc2NfY');
var iv = CryptoJS.enc.Utf8.parse('r0dkbnVQhklNeUGA');
var encrypted = CryptoJS.AES.encrypt(plaintext, 'euhe68vjdr1aX4F091c7aCggSMBf0A7M', key,{iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});
I have found several questions dealing with that problem, but none of them had that extra parameter in encrypt()
between plaintext
and key
.
The CryptoJS wiki does not mention this parameter at all. And yet, it works. I can decrypt with this :
var decrypted = CryptoJS.AES.decrypt(encrypted, 'euhe68vjdr1aX4F091c7aCggSMBf0A7M', key, {iv: iv});
If I omit 'euhe68vjdr1aX4F091c7aCggSMBf0A7M'
in the decrypt()
call, it won't work. So that parameter really does something. But what? Is is a hash, a salt?
Does anybody know how I can replicate this specific encryption process with PHP?
I can not modify this JS code in any way. The code is on a login page and is used to encrypt credentials before they are sent to the server.
I am trying to use PHP-cURL to submit credentials to that server from the command line, which is why I need to reproduce this encryption process.
Update: thanks to Jim's answer, I now have the proper JavaScript, now I need help with replicating the code in PHP
Here is the JS :
var plaintext = 'test';
var key = 'euhe68vjdr1aX4F091c7aCggSMBf0A7M';
var iv = CryptoJS.enc.Utf8.parse('r0dkbnVQhklNeUGA');
var encrypted = CryptoJS.AES.encrypt(plaintext, key, key,{iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7})
encrypted
equals U2FsdGVkX1+ZujoXKqDHcO+4N1QO+Nv1KHUMFjZos1s=
Here is the decryption:
var decrypted = CryptoJS.AES.decrypt(encrypted, key);
decrypted
equals test
I have tried many different ways to replicate the encryption with PHP, but none of them gives a a string I can decode with the above JS.
For instance, using this AES_Encryption class, I tried the following code :
$key = 'euhe68vjdr1aX4F091c7aCggSMBf0A7M';
$iv = 'r0dkbnVQhklNeUGA';
$message = 'test';
$AES = new AES_Encryption($key, $iv, PKCS7);
$encrypted = $AES->encrypt($message);
$decrypted = $AES->decrypt($encrypted);
$base64_encrypted = base64_encode('Salted__'.$encrypted);
I end up with U2FsdGVkX18eEv+TnigBEKGJL8t/V1Hm
instead of U2FsdGVkX1+ZujoXKqDHcO+4N1QO+Nv1KHUMFjZos1s=
Note that both strings start the same way, thanks to the 'Salted__' prefix I added (since CryptoJS seems to do the same thing).
I tried similar code with phpseclib, openssl_encrypt and mcrypt. No luck.
Any hint would be appreciated.
**Update: FIXED **
This PHP code is a perfect match for the CryptoJS code above.
function ssl_encrypt($pass, $data)
{
// Set a random salt
$salt = substr(md5(mt_rand(), true), 8);
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $block - (strlen($data) % $block);
$data = $data . str_repeat(chr($pad), $pad);
// Setup encryption parameters
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, "", MCRYPT_MODE_CBC, "");
$key_len = mcrypt_enc_get_key_size($td);
$iv_len = mcrypt_enc_get_iv_size($td);
$total_len = $key_len + $iv_len;
$salted = '';
$dx = '';
// Salt the key and iv
while (strlen($salted) < $total_len) {
$dx = md5($dx.$pass.$salt, true);
$salted .= $dx;
}
$key = substr($salted,0,$key_len);
$iv = substr($salted,$key_len,$iv_len);
mcrypt_generic_init($td, $key, $iv);
$encrypted_data = mcrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
//return chunk_split(base64_encode('Salted__' . $salt . $encrypted_data),32,"
");
return base64_encode('Salted__' . $salt . $encrypted_data);
}
function ssl_decrypt($password, $edata)
{
$data = base64_decode($edata);
print "Data: " . $data . "
";
$salt = substr($data, 8, 8);
print "Salt (Base64): " . base64_encode($salt) . "
";
$ct = substr($data, 16);
print "Content (Base64): " . base64_encode($ct) . "
";
$rounds = 3;
$data00 = $password.$salt;
print "Data00 (Base64): " . base64_encode($data00) . "
";
$md5_hash = array();
$md5_hash[0] = md5($data00, true);
$result = $md5_hash[0];
print "MD5-Hash[0] (Base64): " . base64_encode($result) . "
";
for ($i = 1; $i < $rounds; $i++) {
$md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true);
$result .= $md5_hash[$i];
print "Result (Base64): " . base64_encode($result) . "
";
}
$key = substr($result, 0, 32);
print "Key (Base64): " . base64_encode($key) . "
";
$iv = substr($result, 32, 16);
print "IV (Base64): " . base64_encode($iv) . "
";
print "Decrypted: " . openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv) . "
";
}
$encryptedString = ssl_encrypt('euhe68vjdr1aX4F091c7aCggSMBf0A7M', 'test');
$decryptedString = ssl_decrypt('euhe68vjdr1aX4F091c7aCggSMBf0A7M', $encryptedString);
I can't remember where I found it. Sorry.
This is a JavaScript thing, not a CryptoJS thing. Try this:
var decrypted = CryptoJS.AES.decrypt(encrypted, 'euhe68vjdr1aX4F091c7aCggSMBf0A7M', {iv: iv});
or this:
key = 'euhe68vjdr1aX4F091c7aCggSMBf0A7M';
var decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv: iv});
You'll see that what you think is the key, isn't the key at all. It's being grouped with the optional parameters at the end, and lost.
'euhe68vjdr1aX4F091c7aCggSMBf0A7M' is your key, or is what your key is derived from.