PHP AES шифрует / расшифровывает

Я нашел пример для en / decoding строк в PHP. Сначала это выглядит очень хорошо, но он не работает 🙁

Кто-нибудь знает, в чем проблема?

$Pass = "Passwort"; $Clear = "Klartext"; $crypted = fnEncrypt($Clear, $Pass); echo "Encrypted: ".$crypted."</br>"; $newClear = fnDecrypt($crypted, $Pass); echo "Decrypted: ".$newClear."</br>"; function fnEncrypt($sValue, $sSecretKey) { return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sDecrypted, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); } function fnDecrypt($sValue, $sSecretKey) { return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sEncrypted), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))); } 

Результат:

Зашифровано: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=

Расшифровано: —‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—

$sDecrypted и $sEncrypted были определены в вашем коде. См. Решение, которое работает ( но не безопасно! ):


СТОП!

Этот пример небезопасен! Не используйте его!


 $Pass = "Passwort"; $Clear = "Klartext"; $crypted = fnEncrypt($Clear, $Pass); echo "Encrypred: ".$crypted."</br>"; $newClear = fnDecrypt($crypted, $Pass); echo "Decrypred: ".$newClear."</br>"; function fnEncrypt($sValue, $sSecretKey) { return rtrim( base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_ECB, mcrypt_create_iv( mcrypt_get_iv_size( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB ), MCRYPT_RAND) ) ), "\0" ); } function fnDecrypt($sValue, $sSecretKey) { return rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_ECB, mcrypt_create_iv( mcrypt_get_iv_size( MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB ), MCRYPT_RAND ) ), "\0" ); } 

Но в этом коде есть и другие проблемы, которые делают его небезопасным, в частности использование ECB (который не является режимом шифрования , только строительный блок, поверх которого могут быть определены режимы шифрования). См . Ответ Fab Sa для быстрого устранения худших проблем и ответа Скотта о том, как это сделать правильно.

Пожалуйста, используйте существующую защищенную библиотеку шифрования PHP

Как правило, это плохая идея написать собственную криптографию, если у вас нет опыта в реализации криптографии других людей.

Ни один из примеров здесь не аутентифицирует зашифрованный текст , что делает их уязвимыми для бит-переписывающих атак.

Если вы можете установить расширения PECL, libsodium еще лучше

 <?php // PECL libsodium 0.2.1 and newer /** * Encrypt a message * * @param string $message - message to encrypt * @param string $key - encryption key * @return string */ function safeEncrypt($message, $key) { $nonce = \Sodium\randombytes_buf( \Sodium\CRYPTO_SECRETBOX_NONCEBYTES ); return base64_encode( $nonce. \Sodium\crypto_secretbox( $message, $nonce, $key ) ); } /** * Decrypt a message * * @param string $encrypted - message encrypted with safeEncrypt() * @param string $key - encryption key * @return string */ function safeDecrypt($encrypted, $key) { $decoded = base64_decode($encrypted); $nonce = mb_substr($decoded, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); $ciphertext = mb_substr($decoded, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); return \Sodium\crypto_secretbox_open( $ciphertext, $nonce, $key ); } 

Затем, чтобы проверить это:

 <?php // This refers to the previous code block. require "safeCrypto.php"; // Do this once then store it somehow: $key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES); $message = 'We are all living in a yellow submarine'; $ciphertext = safeEncrypt($message, $key); $plaintext = safeDecrypt($ciphertext, $key); var_dump($ciphertext); var_dump($plaintext); 

Это можно использовать в любой ситуации, когда вы передаете данные клиенту (например, зашифрованные файлы cookie для сеансов без серверного хранилища, зашифрованные параметры URL и т. Д.) С достаточно высокой степенью уверенности в том, что конечный пользователь не может расшифровывать или надежно изменять с этим.

Поскольку libsodium является кросс-платформенным , это также упрощает общение с PHP, например, Java-апплеты или собственные мобильные приложения.


Примечание. Если вам необходимо добавить зашифрованные файлы cookie с помощью libsodium для вашего приложения, мой работодатель Paragon Initiative Enterprises разрабатывает библиотеку под названием Halite, которая делает все это для вас.

Для информации MCRYPT_MODE_ECB не использует IV (вектор инициализации). Режим ECB делит ваше сообщение на блоки, и каждый блок зашифровывается отдельно. Я действительно не рекомендую .

Режим CBC использует IV, чтобы каждое сообщение было уникальным. CBC рекомендуется и должен использоваться вместо ЕЦБ.

Пример :

 <?php $password = "myPassword_!"; $messageClear = "Secret message"; // 32 byte binary blob $aes256Key = hash("SHA256", $password, true); // for good entropy (for MCRYPT_RAND) srand((double) microtime() * 1000000); // generate random iv $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND); $crypted = fnEncrypt($messageClear, $aes256Key); $newClear = fnDecrypt($crypted, $aes256Key); echo "IV: <code>".$iv."</code><br/>". "Encrypred: <code>".$crypted."</code><br/>". "Decrypred: <code>".$newClear."</code><br/>"; function fnEncrypt($sValue, $sSecretKey) { global $iv; return rtrim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv)), "\0\3"); } function fnDecrypt($sValue, $sSecretKey) { global $iv; return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_CBC, $iv), "\0\3"); } 

Вы должны запастись IV, чтобы декодировать каждое сообщение (IV не являются секретными). Каждое сообщение уникально, потому что каждое сообщение имеет уникальный IV.

  • Дополнительная информация о режиме работы (википедия) .

Немного важных вещей, которые следует учитывать при шифровании AES:

  1. Никогда не используйте обычный текст в качестве ключа шифрования. Всегда хешируйте обычный текстовый ключ, а затем используйте для шифрования.
  2. Всегда используйте Random IV (вектор инициализации) для шифрования и дешифрования. Истинная рандомизация важна.
  3. Как упоминалось выше, не используйте режим ecb , используйте CBC .

Если вы используете MCRYPT_RIJNDAEL_128, попробуйте rtrim($output, "\0\3") . Если длина строки меньше 16, функция decrypt вернет строку длиной 16 символов, добавив 03 в конец.

Вы можете легко проверить это, например, попробовав:

 $string = "TheString"; $decrypted_string = decrypt_function($stirng, $key); echo bin2hex($decrypted_string)."=".bin2hex("TheString"); 

Вы можете использовать OpenSSL для простого и безопасного шифрования AES в PHP. OpenSSL обычно встроен в PHP, поэтому нет необходимости в внешних зависимостях. Следующий пример:

  • использует AES256 в режиме CBC
  • генерирует ключ из предоставленного пароля, используя SHA256
  • генерирует hmac-хэш зашифрованных данных для проверки целостности
  • генерирует случайный IV для каждого сообщения
  • добавляет IV (16 байт) и хэш (32 байта) к зашифрованному тексту

IV является общедоступной информацией и должна быть случайной для каждого сообщения. Хэш гарантирует, что данные не были подделаны.

 function encrypt($plaintext, $password) { $method = "AES-256-CBC"; $key = hash('sha256', $password, true); $iv = openssl_random_pseudo_bytes(16); $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv); $hash = hash_hmac('sha256', $ciphertext, $key, true); return $iv . $hash . $ciphertext; } function decrypt($ivHashCiphertext, $password) { $method = "AES-256-CBC"; $iv = substr($ivHashCiphertext, 0, 16); $hash = substr($ivHashCiphertext, 16, 32); $ciphertext = substr($ivHashCiphertext, 48); $key = hash('sha256', $password, true); if (hash_hmac('sha256', $ciphertext, $key, true) !== $hash) return null; return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv); } 

Применение:

 $encrypted = encrypt('Plaintext string.', 'password'); // this yields a binary string echo decrypt($encrypted, 'password'); // decrypt($encrypted, 'wrong password') === null 

Это должно быть совместимо с другими реализациями AES, но не с mcrypt , поскольку mcrypt использует PKCS # 5 вместо PKCS # 7.