Шифрование данных паролем

Теги: php

Допустим у нас есть данные:

$data = 'My secret data!';

И мы хотим зашифровать их, чтобы позже расшифровать с использованием пароля, например такого:

$password = '1234';

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

$key = sha1($password);

Рекомендуется использовать для шифрования библиотеку OpenSSL: openssl_encrypt(), openssl_decrypt() и вспомогательные функции.

Для начала нужно выбрать метод шифрования, например, aes-128-cbc. Все доступные методы можно посмотреть вызвав функцию openssl_get_cipher_methods().

$method = 'aes-128-cbc';

Теперь нам нужно создать вектор инициализации (IV, Initialization Vector) - случайные данные, на основе которых будет произведено шифрование.

// определяем длину iv для выбранного метода шифрования
$ivSize = openssl_cipher_iv_length($method);

// генерируем iv необходимой длины
$iv = openssl_random_pseudo_bytes($ivSize);

Всё готово к шифрованию. Вызываем метод openssl_encrypt():

$encrypted = openssl_encrypt(
    $data,
    $method,
    $key,
    // передаём флаг OPENSSL_RAW_DATA,
    // чтобы openssl не прогнал результат через base64 и вернул его как есть
    OPENSSL_RAW_DATA,
    $iv
);

Добавляем iv в результат шифрования, чтобы мы могли использовать его при расшифровке:

$encrypted = $iv . $encrypted;

Теперь прогоняем результат через base64:

$encrypted = base64_encode($encrypted); // '4b7zpIa4IsTxiQWOsSCkC89urh/vkFtaG4O9Vcr1uV8='

Зашифрованная строка готова, её мы можем передавать во вне. И когда эта строка придёт к нам, мы сможем её расшифровать.

Декодируем base64:

$encrypted = base64_decode($encrypted);

Теперь получим из строки iv:

// определяем длину iv для выбранного метода шифрования
$ivSize = openssl_cipher_iv_length($method);

// вырезаем iv из зашифрованных данных
$iv = substr($encrypted, 0, $ivSize);

Функция substr будет корректно работать с битовыми строками только если в настройках php указано mbstring.func_overload = 0, для остальных вариантов нужно использовать mb_substr и mb_strlen, указав кодировку 8bit для OpenSSL или iso-8859-1 для Mcrypt. Готовый код с переключением на mb_ функции можно взять в моём гитхабе hobocta/encrypt.

Убираем из зашифрованной строки iv:

$encrypted = substr($encrypted, $ivSize);

И вызываем openssl_decrypt():

$decrypted = openssl_decrypt(
    $encrypted,
    $method,
    $key,
    OPENSSL_RAW_DATA,
    $iv
);

Выводим результат:

echo $decrypted; // 'My secret data!'

Если же библиотека OpenSSL недоступна и её никак нельзя установить, то в крайнем случае мы можем использовать аналогичный функционал из библиотеки Mcrypt.

В Mcrypt нам сначала тоже нужно выбрать метод шифрования. Выберем, например, MCRYPT_RIJNDAEL_128 с режимом MCRYPT_MODE_CBC. Все доступные методы тут, а режимы тут.

$cipher = MCRYPT_RIJNDAEL_128;
$mode = MCRYPT_MODE_CBC;
$ivSource = MCRYPT_RAND; // источник случайных данных

Генерируем iv:

$ivSize = mcrypt_get_iv_size($cipher, $mode);
$iv = mcrypt_create_iv($ivSize, $ivSource);

Шифруем данные:

$encrypted = mcrypt_encrypt(
    $cipher,
    $key,
    $data,
    $mode,
    $iv
);

Добавляем iv в шифрованные данные и кодируем в base64:

$encrypted = $iv . $encrypted;
$encrypted = base64_encode($encrypted); // 'HJGnojVdzFAQAHYqkwNOd9pfokMMs7GJfN5xAqVguMw='

Для того, чтобы расшифровать нам останется выполнить следующее:

// получаем из данных iv
$iv = substr($encrypted, 0, $ivSize);

// убираем из данных iv
$encrypted = substr($encrypted, $ivSize);

// расшифровываем
$decrypted = mcrypt_decrypt(
    $cipher,
    $key,
    $encrypted,
    $mode,
    $iv
);
// в результате мы получим данные с двумя лишними символами вконце:
var_export($decrypted); // 'My secret data!' . "\0" . ''

// убираем лишние символы:
$decrypted = rtrim($decrypted, "\0\4");

// всё готово
var_export($decrypted); // 'My secret data!'

Всё это с удобной оберткой можно взять в моём гитхабе: hobocta/encrypt