Я нашел пример для 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 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:
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, поэтому нет необходимости в внешних зависимостях. Следующий пример:
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.