Обратимое шифрование по ключу на PHP

Вся информация и фото, взяты с https://codernotes.ru/articles/php/obratimoe-shifrovanie-po-klyuchu-na-php.html

Задача надежного шифрования текстовой информации часто встречается при программировании сайтов. В зашифрованном виде бывает необходимо хранить не только пароли, но и другую информацию. Недавно такая задача встала и у меня. Мне нужна была более-менее надежная функция обратимого шифрования текста по ключу. Почему по ключу? Дело в том, что шифрация без ключа может быть взломана, т.к. большинство алгоритмов шифрования можно найти в интернете и подобрать способ, чтобы получить исходные данные, а шифрация с ключом гораздо более надежная.

Обратимое шифрование на PHP 5 библиотекой MCrypt

Поискав по интернету я нашел целых 2 достаточно коротких в плане количества кода и в тоже время очень надежных способа обратимого шифрования по ключу, которые использует встроенную в php библиотеку Mcrypt.

На подавляющем большинстве хостингов данная библиотека сразу же идет вместе с php. Но если вы администрируете свой сервер и данной библиотеки почему-то вдруг не оказалось в составе php, вы всегда можете ее доустановить командой apt-get install php5-mcrypt для Debian-подобных систем (в т.ч. Mint, Ubuntu) или yum install php-mcrypt для RedHat-подобных систем (в т.ч. Fedora, openSUSE, CentOS) или любым другим способом, который вам нравится (через dpkg, rpm, yast и т.д.).

Затем в папке /etc находите папку php, содержащую ini-файлы расширений, загружаемых php по-умолчанию. Посмотреть путь до этой папки можно в php.ini в разделе «Dynamic Extensions». Это может быть папка /etc/php или /etc/php5/mods-available/ или как у меня на сервере /etc/php.d (вообщем, зависит от настроек php). В этой папке должен присутствовать файл mcrypt.ini. Если его там нет, тогда создайте его с таким содержимым:

; Enable mcrypt extension module
extension=mcrypt.so

После этого можно включить расширение командой php5enmod mcrypt, а затем перезапустить сервер /etc/init.d/apache2 restart для Debian-систем или service httpd restart для RedHat систем. Разумеется, все описанные действия выполняются с правами root-а.

Итак, теперь приведу примеры 2-х способов шифрования по ключу, которые я нашел для себя в интернете.

1-й способ. Обратимое шифрование по произвольному ключу.

?

1234567891011121314151617181920212223242526define('ENCRYPTION_KEY', '_91X:s+{a2Jwb6*J');$txt= 'Тестируем обратимое шифрование на php';$encrypted= encrypt($txt, ENCRYPTION_KEY);echo$encrypted.'<br>';$decrypted= decrypt($encrypted, ENCRYPTION_KEY);echo$decrypted;functionencrypt($decrypted, $key) {  $ekey= hash('SHA256', $key, true);  srand(); $iv= mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);  if(strlen($iv_base64= rtrim(base64_encode($iv), '=')) != 22) returnfalse;  $encrypted= base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $ekey, $decrypted. md5($decrypted), MCRYPT_MODE_CBC, $iv));  return$iv_base64. $encrypted;}functiondecrypt($encrypted, $key) {  $ekey= hash('SHA256', $key, true);  $iv= base64_decode(substr($encrypted, 0, 22) . '==');  $encrypted= substr($encrypted, 22);  $decrypted= rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $ekey, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4");  $hash= substr($decrypted, -32);  $decrypted= substr($decrypted, 0, -32);  if(md5($decrypted) != $hash) returnfalse;  return$decrypted;}

2-й способ. Очень надежное обратимое шифрование по шестандцатиричному ключу.

Внимание! Ключ должен быть шестандцатиричным (символы 0123456789ABCDEF) длиной 32 или 64 символа.

?

12345678910111213141516171819202122232425262728293031323334define('ENCRYPTION_KEY', 'e3f080b6edfcf6fff70654021c7c2e43');$txt= 'Тестируем обратимое шифрование на php';$encrypted= mc_encrypt($txt, ENCRYPTION_KEY);echo$encrypted.'<br>';$decrypted= mc_decrypt($encrypted, ENCRYPTION_KEY);echo$decrypted;// Encrypt Functionfunctionmc_encrypt($encrypt, $key) {  $encrypt= serialize($encrypt);  $iv= mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);  $key= pack('H*', $key);  $mac= hash_hmac('sha256', $encrypt, substr(bin2hex($key), -32));  $passcrypt= mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $encrypt.$mac, MCRYPT_MODE_CBC, $iv);  $encoded= base64_encode($passcrypt).'|'.base64_encode($iv);  return$encoded;}// Decrypt Functionfunctionmc_decrypt($decrypt, $key) {  $decrypt= explode('|', $decrypt.'|');  $decoded= base64_decode($decrypt[0]);  $iv= base64_decode($decrypt[1]);  if(strlen($iv)!==mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC)){ returnfalse; }  $key= pack('H*', $key);  $decrypted= trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $decoded, MCRYPT_MODE_CBC, $iv));  $mac= substr($decrypted, -64);  $decrypted= substr($decrypted, 0, -64);  $calcmac= hash_hmac('sha256', $decrypted, substr(bin2hex($key), -32));  if($calcmac!==$mac){ returnfalse; }  $decrypted= unserialize($decrypted);  return$decrypted;}

Примечательно, что если выполнить приведенные мной примеры несколько раз, мы увидим каждый раз разные зашифрованные данные, хотя шифровался один и тот же текст. При этом, хоть и зашифрованные данные выглядят по-разному, результат расшифровки всегда один и тот же — исходный текст. Подробнее почитать про библиотеку шифрования можно на php.net. На ее основе можно придумать и свой, уникальный способ обратимого шифрования.

Обратимое шифрование на PHP 7 библиотекой OpenSSL

Функции библиотеки Mcrypt, такие как mcrypt_encrypt и mcrypt_decrypt считаются устаревшими и не рекомендуют их использовать. Начиная с PHP 7.2 библиотеку Mcrypt перенесли в PECL. Вместо MCrypt предлагается использовать openssl_encrypt и openssl_decrypt из библиотеки OpenSSL.

Если всё же хотите подключить MCrypt в PHP 7.2 или выше, чтобы использовать ее функции шифрования, тогда можете посмотреть эту статью.

Однако, вернемся к библиотеке OpenSSL. Эта библиотека содержит множество различных методов и алгоритмов симметричного и асимметричного шифрования. Пока что приведу один пример шифрования этими функциями, взятый с php.net а в будущем, доработаю статью, добавив еще примеры.

?

1234567891011121314151617181920212223define('ENCRYPTION_KEY', 'ab86d144e3f080b61c7c2e43');// Encrypt$plaintext= "Тестируем обратимое шифрование на php 7";$ivlen= openssl_cipher_iv_length($cipher="AES-128-CBC");$iv= openssl_random_pseudo_bytes($ivlen);$ciphertext_raw= openssl_encrypt($plaintext, $cipher, ENCRYPTION_KEY, $options=OPENSSL_RAW_DATA, $iv);$hmac= hash_hmac('sha256', $ciphertext_raw, ENCRYPTION_KEY, $as_binary=true);$ciphertext= base64_encode( $iv.$hmac.$ciphertext_raw);echo$ciphertext.'<br>';// Decrypt$c= base64_decode($ciphertext);$ivlen= openssl_cipher_iv_length($cipher="AES-128-CBC");$iv= substr($c, 0, $ivlen);$hmac= substr($c, $ivlen, $sha2len=32);$ciphertext_raw= substr($c, $ivlen+$sha2len);$plaintext= openssl_decrypt($ciphertext_raw, $cipher, ENCRYPTION_KEY, $options=OPENSSL_RAW_DATA, $iv);$calcmac= hash_hmac('sha256', $ciphertext_raw, ENCRYPTION_KEY, $as_binary=true);if(hash_equals($hmac, $calcmac)){    echo$plaintext;}

Обратите внимание: этот алгоритм будет работать начиная с PHP 5.6 и выше. На предыдущих версиях будет выдавать ошибку из-за функции hash_equals, которая осуществляет сравнение строк нечувствительное к атакам по времени (подробнее про атаки по времени можете почитать на википедии).

Другими альтернативами для шифрования на PHP 7+ являются библиотеки: Libsodium и defuse/php-encryption.