I'm encrypting my user password in JavaScript like this:
var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
It works fine but now I'm trying to decrypt in PHP on the server side like this:
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($password), MCRYPT_MODE_CBC, $iv);
it doesn't works at all, the decrypted password string looks very strange:
string(64) ">�OX2MS��댗v�<$�ʕ��i�̄��_��P���\�կ=�_6(�m����,4WT7��a"
Here is the current state of my code in JavaScript after the helpful comments:
var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
var ivHex = encryptedPassword.iv.toString();
var ivSize = encryptedPassword.algorithm.ivSize; // same as blockSize
var keySize = encryptedPassword.algorithm.keySize;
var keyHex = encryptedPassword.key.toString();
var saltHex = encryptedPassword.salt.toString(); // must be sent
var openSslFormattedCipherTextString = encryptedPassword.toString(); // not used
var cipherTextHex = encryptedPassword.ciphertext.toString(); // must be sent
I am sending saltHex and CipherTextHex to the PHP server and I'm using mcrypt_decrypt() like this:
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), $saltHex);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($cipherTextHex), MCRYPT_MODE_CBC, $iv);
It still does't work with this updated code.
Can someone help me to decrypt properly with mcrypt_decrypt() PHP function for a simple AES encryption method ? I'm sure I am doing something wrong with the cipher, mcrypt mode and the IV parameters inside my mcrypt_decrypt() method. Thanks if you know.
In PHP, Encryption and Decryption of a string is possible using one of the Cryptography Extensions called OpenSSL function for encrypt and decrypt. openssl_encrypt() Function: The openssl_encrypt() function is used to encrypt the data. Parameters: $data: It holds the string or data which need to be encrypted.
Secret Key Encryption is also called Symmetric encryption, The Secret Key Encryption of the PHP uses just one key, called a shared secret, for both encrypting and decrypting. To encrypt the data, Here one same key is used by the sender (for encryption) and the receiver (for decryption).
Secret key encryption (or symmetric encryption as it's also known) uses a single key to both encrypt and decrypt data. In the past PHP relied on mcrypt and openssl for secret key encryption. PHP 7.2 introduced Sodium, which is more modern and widely considered more secure.
The problem is that in the CryptoJS code a password is used to derive the key and the IV to be used for AES encryption, but mcrypt only uses the key to encrypt/decrypt. This information needs to be passed to php. Since you don't want to transmit the password, you have to derive the key and IV in the same way in php.
The following code derives the key and IV from a password and salt. It is modeled after the code in my answer here (for more information).
function evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5") {
$targetKeySize = $keySize + $ivSize;
$derivedBytes = "";
$numberOfDerivedWords = 0;
$block = NULL;
$hasher = hash_init($hashAlgorithm);
while ($numberOfDerivedWords < $targetKeySize) {
if ($block != NULL) {
hash_update($hasher, $block);
}
hash_update($hasher, $password);
hash_update($hasher, $salt);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
// Iterations
for ($i = 1; $i < $iterations; $i++) {
hash_update($hasher, $block);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
}
$derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4));
$numberOfDerivedWords += strlen($block)/4;
}
return array(
"key" => substr($derivedBytes, 0, $keySize * 4),
"iv" => substr($derivedBytes, $keySize * 4, $ivSize * 4)
);
}
The salt is generated during encryption in CryptoJS and needs to be sent to php with the ciphertext. Before invoking evpKDF
the salt has to be converted to a binary string from hex.
$keyAndIV = evpKDF("Secret Passphrase", hex2bin($saltHex));
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$keyAndIV["key"],
hex2bin($cipherTextHex),
MCRYPT_MODE_CBC,
$keyAndIV["iv"]);
If only encryptedPassword.toString()
was sent to the server, then it is necessary to split the salt and actual ciphertext before use. The format is a proprietary OpenSSL-compatible format with the first 8 bytes being "Salted__", the next 8 bytes being the random salt and the rest is the actual ciphertext. Everything together is Base64-encoded.
function decrypt($ciphertext, $password) {
$ciphertext = base64_decode($ciphertext);
if (substr($ciphertext, 0, 8) != "Salted__") {
return false;
}
$salt = substr($ciphertext, 8, 8);
$keyAndIV = evpKDF($password, $salt);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$keyAndIV["key"],
substr($ciphertext, 16),
MCRYPT_MODE_CBC,
$keyAndIV["iv"]);
// unpad (PKCS#7)
return substr($decryptPassword, 0, strlen($decryptPassword) - ord($decryptPassword[strlen($decryptPassword)-1]));
}
The same can be achieved with the OpenSSL extension instead of Mcrypt:
function decrypt($ciphertext, $password) {
$ciphertext = base64_decode($ciphertext);
if (substr($ciphertext, 0, 8) != "Salted__") {
return false;
}
$salt = substr($ciphertext, 8, 8);
$keyAndIV = evpKDF($password, $salt);
$decryptPassword = openssl_decrypt(
substr($ciphertext, 16),
"aes-256-cbc",
$keyAndIV["key"],
OPENSSL_RAW_DATA, // base64 was already decoded
$keyAndIV["iv"]);
return $decryptPassword;
}
/**
*-------------PHP code example-----------------
*/
/**
* Decrypt data from a CryptoJS json encoding string
*
* @param mixed $passphrase
* @param mixed $jsonString
* @return mixed
*/
function cryptoJsAesDecrypt($passphrase, $jsonString){
$jsondata = json_decode($jsonString, true);
$salt = hex2bin($jsondata["s"]);
$ct = base64_decode($jsondata["ct"]);
$iv = hex2bin($jsondata["iv"]);
$concatedPassphrase = $passphrase.$salt;
$md5 = array();
$md5[0] = md5($concatedPassphrase, true);
$result = $md5[0];
for ($i = 1; $i < 3; $i++) {
$md5[$i] = md5($md5[$i - 1].$concatedPassphrase, true);
$result .= $md5[$i];
}
$key = substr($result, 0, 32);
$data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
return json_decode($data, true);
}
/**
* Encrypt value to a cryptojs compatiable json encoding string
*
* @param mixed $passphrase
* @param mixed $value
* @return string
*/
function cryptoJsAesEncrypt($passphrase, $value){
$salt = openssl_random_pseudo_bytes(8);
$salted = '';
$dx = '';
while (strlen($salted) < 48) {
$dx = md5($dx.$passphrase.$salt, true);
$salted .= $dx;
}
$key = substr($salted, 0, 32);
$iv = substr($salted, 32,16);
$encrypted_data = openssl_encrypt(json_encode($value), 'aes-256-cbc', $key, true, $iv);
$data = array("ct" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt));
return json_encode($data);
}
$encrypted = '{"ct":"nPfd1U0y9o2hRCdwJK6XkM1E01wa1ZjMu3eAzGjUD60=","iv":"2abda27fc571cf74e6efc1ba564801f9","s":"813a340e805f54ae"}';
$key = "123456";
$decrypted = cryptoJsAesDecrypt($key, $encrypted);
/* -------------Javascript code example-----------------*/
var CryptoJSAesJson = {
stringify: function (cipherParams) {
var j = {ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)};
if (cipherParams.iv) j.iv = cipherParams.iv.toString();
if (cipherParams.salt) j.s = cipherParams.salt.toString();
return JSON.stringify(j);
},
parse: function (jsonStr) {
var j = JSON.parse(jsonStr);
var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(j.ct)});
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv)
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s)
return cipherParams;
}
}
var key = "123456";
var encrypted = CryptoJS.AES.encrypt(JSON.stringify("value to encrypt"), key, {format: CryptoJSAesJson}).toString();
console.log(encrypted);
var decrypted = JSON.parse(CryptoJS.AES.decrypt(encrypted, key, {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8));
console.log("decryyepted: "+decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With