Простая и надежная PHP функция для обратимого шифрования
Понедельник, 24/09/2007Всем привет!
Сегодня столкнулся с такой проблемой, нужно было шифровать ключи в базе данных паролем администратора. А тут, вот те раз, оказалось, что в PHP нет встроенной функции обратимого шифрования, даже самой простой. “Хм…” подумал я и полез в инет.
Как оказалось в инете с этим тоже глухо, не нашел при беглом взгяде ни одной готовой процедуры или скрипта. Правда нашел библиотеку к PHP для шифрования - mcrypt, но что бы ее поставить, нужно пересобирать ядро PHP или просить поставить техподдержку хостинга эту библиотеку и не на всех хостингах это возможно, в общем с mcrypt настоящий гемммморой.
Не мудурствуя лукаво, решил просто сесть и быстро написать свою функцию обратимомого шифрования. Тем более мне не шифровки нужно в центр слать с геополитической обстановкой на кухне замполита, где коллосальные требования к криптостойкости, а мне нужна просто более менее надежная защита некоторых полей базы данных.
Итак подумав решил организовать классическое и эффективное наложение шифруемой строки на надежную гамму (псевдослучайную последовательность) с помощью XOR. Вопрос только, где взять приемлимую по надежности гамму. Конгруэнтные генераторы отлетают сразу. Регистры сдвига долго, да и легко они ломаются по куску открытого текста. В общем подумав еще минут 10 придумал использовать для генерации гаммы обычную хэш функцию.
Т.е. математика такая:
K(i+1) = sha1(K[i])
Но, если вдуматься, в таком виде ее использовать для гаммы нельзя, ибо такая гамма будет уязвима к куску открытого текста размером примерно в 20 байт (sha1 выдает хэш размером в 20 байт).
Однако если для гаммы брать только, например, первый байт от K[i] (сам он по себе 20 байт), то проблема уходит. Однако встает вопрос производительности на относительно больших текстах.
По этому я решил брать первые 8 байт (можно меньше или больше - на ваш выбор и соответственно решаемой задаче). В этом случае криптостойкость (по открытому куску текста) пораждаемой гаммы будет составлять сложность взлома sha1 по оставшимся 12 байтам от 20. Что более чем достаточно для большинства задач.
Реализовав это все в код у меня получилось:
//PHP функция для обратимого шифрования
//————————————-
function encode($String, $Password)
{
$Salt=’BGuxLWQtKweKEMV4′;
$StrLen = strlen($String);
$Seq = $Password;
$Gamma = ”;
while (strlen($Gamma)< $StrLen)
{
$Seq = pack(”H*”,sha1($Seq.$Salt)); //в PHP5 эту строку можно заменить на $Seq = sha1($Seq.$Salt, true);
$Gamma.=substr($Seq,0,8);
}return $String^$Gamma;
}
Переменную $Salt=’BGuxLWQtKweKEMV4′; я ввел для того, что бы случайно не возникло коллизии, если вы будете хранить пароль, которым шифровали рядом в виде хэша (того же sha1). В принципе, вы можете подставить в нее свое значение, главное, что бы она была не пустая!
Ну и еще один момент, если хотите еще усилить криптостойкость ко взлому, то можно строку $Seq = pack(”H*”,sha1($Seq.$Salt)); переделать на такую: $Seq = pack(”H*”,sha1($Gamma.$Seq.$Salt)); что сделает бесполезной попытку взлома по открытому куску текста из середины.
В общем, в конце концов у меня получилось так:
//PHP функция для обратимого шифрования
//————————————-
function encode($String, $Password)
{
$Salt=’BGuxLWQtKweKEMV4′;
$StrLen = strlen($String);
$Seq = $Password;
$Gamma = ”;
while (strlen($Gamma)< $StrLen)
{
$Seq = pack(”H*”,sha1($Gamma.$Seq.$Salt));
$Gamma.=substr($Seq,0,8);
}return $String^$Gamma;
}
Пользуйтесь на здоровье!
Ах да, совсем громадные объемы текста этой функцией лучше не шифровать, т.к. у хешей последовательности кончаются короткими циклами, но это нужно очень постараться с размером шифровки, что бы дойти до этих “коротких циклов”.
Хотя вы можете поэкспериментировать и доабгрейдить этот код, как вам будет необходимо, благо он простой, понятный и достаточно надежный.
Надеюсь, вам смог помочь этот пост. А если у вас есть пожелания замечания к моим выкладкам обязательно пишите, буду очень рад!
С уважением, Владимир.
Добавлено 9 марта 2010
Поскольку топик вызвал интерес и некоторое вопросы о том, как же все-таки расшифровывать и зашифровывать, решил сделать добавку к топику и выложить более расширеннй код.
В расширенном коде есть две процедуры encode и decode, а также встроена простая проверка правильности расшифровки (один байт):
function encode($String, $Password)
{
//Author: Vladimir Kim (www.vkim.ru) 2010
//Free for use
if (!$Password) die ("Не задан пароль шифрования");
$Salt='BGuxLWQtKweKEMV4';
$String = substr(pack("H*",sha1($String)),0,1).$String;
$StrLen = strlen($String);
$Seq = $Password;
$Gamma = '';
while (strlen($Gamma)< $StrLen)
{
$Seq = pack("H*",sha1($Seq.$Gamma.$Salt));
$Gamma.=substr($Seq,0,8);
}
return base64_encode($String^$Gamma);
}
function decode($String, $Password)
{
//Author: Vladimir Kim (www.vkim.ru) 2010
//Free for use
if (!$Password) die ("Не задан пароль для расшифровки");
$Salt='BGuxLWQtKweKEMV4';
$StrLen = strlen($String);
$Seq = $Password;
$Gamma = '';
while (strlen($Gamma)<$StrLen)
{
$Seq = pack("H*",sha1($Seq.$Gamma.$Salt));
$Gamma.=substr($Seq,0,8);
}
$String = base64_decode($String);
$String = $String^$Gamma;
$DecodedString = substr($String, 1);
$Error = ord(substr($String, 0, 1)
^ substr(pack("H*",sha1($DecodedString)),0,1));
//проверяем
if ($Error) return false;
else return $DecodedString;
}
Вот, если будут вопросы, задавайте
С уважением, Владимир
