Я шифрую свой пароль пользователя в JavaScript следующим образом:
var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
Он отлично работает, но теперь я пытаюсь расшифровать 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);
он вообще не работает, расшифрованная строка пароля выглядит очень странно:
string(64) "> OX2MS 댗v <$ ʕ i ̄ _ P \ կ= _6( m ,4WT7 a"
Вот текущее состояние моего кода в 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
Я посылаю 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);
Он по-прежнему не работает с этим обновленным кодом.
Может ли кто-нибудь помочь мне правильно расшифровать функцию mcrypt_decrypt () PHP для простого метода шифрования AES? Я уверен, что я делаю что-то неправильно с шифрованием, режимом mcrypt и параметрами IV внутри моего метода mcrypt_decrypt (). Спасибо, если вы знаете.
Проблема в том, что в коде CryptoJS используется пароль для получения ключа и IV, который будет использоваться для шифрования AES, но mcrypt использует только ключ для шифрования / дешифрования. Эта информация должна быть передана php. Поскольку вы не хотите передавать пароль, вы должны вывести ключ и IV таким же образом в php.
Следующий код выводит ключ и 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_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])); }
То же самое можно сделать с расширением 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; }
Вы не можете расшифровать со случайным вектором инициализации – вам нужно использовать тот же IV, за который были зашифрованы данные. Кроме того, IIRC, AES по умолчанию имеет 8-битное представление зашифрованных данных, которые необходимо будет тщательно обрабатывать при передаче по HTTP.