JavaScript加密和PHP解密

时间:2023-02-07 18:30:12

I'm encrypting my user password in JavaScript like this:

我在JavaScript中加密我的用户密码,如下所示:

 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:

它工作正常,但现在我试图在服务器端的PHP解密像这样:

 $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:

以下是有用评论后我的JavaScript代码的当前状态:

    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:

我将saltHex和CipherTextHex发送到PHP服务器,我正在使用mcrypt_decrypt(),如下所示:

 $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.

有人帮我用mcrypt_decrypt()PHP函数正确解密一个简单的AES加密方法吗?我确定我在mcrypt_decrypt()方法中使用了密码,mcrypt模式和IV参数。谢谢,如果你知道。

2 个解决方案

#1


9  

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.

问题是在CryptoJS代码中,密码用于导出密钥和用于AES加密的IV,但mcrypt仅使用密钥加密/解密。这些信息需要传递给php。由于您不想传输密码,因此您必须在php中以相同的方式派生密钥和IV。

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).

以下代码从密码和salt派生密钥和IV。它是在我的答案中的代码之后建模的(有关更多信息)。

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.

在CryptoJS中加密期间生成salt,需要使用密文将其发送到php。在调用evpKDF之前,必须将salt转换为十六进制的二进制字符串。

$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.

如果只将encryptedPassword.toString()发送到服务器,则必须在使用前拆分salt和实际密文。格式是专有的OpenSSL兼容格式,前8个字节为“Salted __”,接下来的8个字节为随机盐,其余为实际密文。所有东西都是Base64编码的。

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:

使用OpenSSL扩展而不是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;}

#2


0  

You can't decrypt with a random initialisation vector - you need to use the same IV the data was encrypted with. Also, IIRC, AES defaults to an 8 bit representation of the encrypted data which will need to be handled carefully in transfer over HTTP.

您无法使用随机初始化向量进行解密 - 您需要使用相同的IV来加密数据。此外,IIRC,AES默认为加密数据的8位表示,需要在通过HTTP传输时小心处理。

#1


9  

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.

问题是在CryptoJS代码中,密码用于导出密钥和用于AES加密的IV,但mcrypt仅使用密钥加密/解密。这些信息需要传递给php。由于您不想传输密码,因此您必须在php中以相同的方式派生密钥和IV。

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).

以下代码从密码和salt派生密钥和IV。它是在我的答案中的代码之后建模的(有关更多信息)。

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.

在CryptoJS中加密期间生成salt,需要使用密文将其发送到php。在调用evpKDF之前,必须将salt转换为十六进制的二进制字符串。

$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.

如果只将encryptedPassword.toString()发送到服务器,则必须在使用前拆分salt和实际密文。格式是专有的OpenSSL兼容格式,前8个字节为“Salted __”,接下来的8个字节为随机盐,其余为实际密文。所有东西都是Base64编码的。

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:

使用OpenSSL扩展而不是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;}

#2


0  

You can't decrypt with a random initialisation vector - you need to use the same IV the data was encrypted with. Also, IIRC, AES defaults to an 8 bit representation of the encrypted data which will need to be handled carefully in transfer over HTTP.

您无法使用随机初始化向量进行解密 - 您需要使用相同的IV来加密数据。此外,IIRC,AES默认为加密数据的8位表示,需要在通过HTTP传输时小心处理。