问题在于,在CryptoJS代码中,密码用于导出密钥,而IV用于AES加密,但是mcrypt仅使用密钥进行加密/解密。此信息需要传递给php。由于您不想传输密码,因此必须在php中以相同的方式派生密钥和IV。
以下代码从密码和盐中导出密钥和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) );}盐是在CryptoJS加密期间生成的,需要与密文一起发送到php。在调用
evpKDF盐之前,必须先将其从十六进制转换为二进制字符串。
$keyAndIV = evpKDF("Secret Passphrase", hex2bin($saltHex));$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $keyAndIV["key"], hex2bin($cipherTextHex), MCRYPT_MODE_CBC, $keyAndIV["iv"]);如果仅将
encryptedPassword.toString()其发送到服务器,则有必要在使用前将盐和实际密文分开。该格式是与OpenSSL兼容的专有格式,前8个字节为“
Salted__”,后8个字节为随机盐,其余为实际密文。所有内容都经过base64编码。
function decrypt($ciphertext, $password) { $ciphertext = base64_depre($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]));}使用OpenSSL扩展而不是Mcrypt可以实现相同的目的:
function decrypt($ciphertext, $password) { $ciphertext = base64_depre($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 depred $keyAndIV["iv"]); return $decryptPassword;}


