Шифрование алгоритмом XXTEA на PHP
Сначала всех читателей и посетителей нашего блога хотелось бы поздравить с наступившими новогодними праздниками и еще раз пожелать здоровья, удачи и успехов во всех делах!
Сегодня я начну рассматривать тему шифрования, которая ни разу еще не была рассмотрена в нашем блоге. Но начну я не с расширений PECL и встроенных возможностей в PHP, а с простого алгоритма шифрования под названием TEA, а точнее предоставлю совсем небольшую библиотеку для симметричного шифрования для модификации этого алгоритма под названием XXTEA.
Кстати, о симметричности. Для тех, кто не знает: шифрование бывает симметричным и асимметричным. Не будем пока забивать голову различиями. О них мы поговорим в одной из следующих статей. Скажу лишь пока только, что асимметричное шифрование предназначено для обмена информацией между источником и приемником и требует для своей работы два ключа – открытый и закрытый. Асимметричные шифры обычно работают медленнее. Симметричное же шифрование требует для шифрования один единственный ключ и в основном используется для шифрования файлов и информации, хранимой на различных источниках.
Алгоритм TEA
По определению , Tiny Encryption Algorithm (TEA) — блочный алгоритм шифрования типа «», представленный в 1994 году Дэвидом Уилером (David Wheeler) и Роджером Нидхэмом (Roger Needham).
У этого алгоритма в чистом виде были найдены уязвимости, поэтому у него существуют две усовершенствованные модификации: XTEA и XXTEA (закрывает недостатки XTEA). XXTEA является на момент написания статьи последней модификацией, поэтому мы будем рассматривать именно ее.
Вообще, про шифрование можно сказать очень многое – например, о длинах ключей (кстати, длина ключа в XXTEA = 128 бит) или режимах шифрования. Но все это в будущих статьях
Реализация XXTEA
Реализацию алгоритма XXTEA можно найти для множества языков программирования на просторах Интернета, но для PHP чистую реализацию (без использования ) я нашел только у китайцев
– на страничке
Спасибо китайским ребятам!
Вот небольшой PHP-код, позволяющий шифровать данные алгоритмом XXTEA:
<?php
/* XXTEA encryption arithmetic library.
*
* Copyright (C) 2006 Ma Bingyao <andot@ujn.edu.cn>
* Version: 1.5
* LastModified: Dec 5, 2006
* This library is free. You can redistribute it and/or modify it.
*/
function long2str($v, $w) {
$len = count($v);
$n = ($len - 1) << 2;
if ($w) {
$m = $v[$len - 1];
if (($m < $n - 3) || ($m > $n)) return false;
$n = $m;
}
$s = array();
for ($i = 0; $i < $len; $i++) {
$s[$i] = pack("V", $v[$i]);
}
if ($w) {
return substr(join('', $s), 0, $n);
} else {
return join('', $s);
}
}
function str2long($s, $w) {
$v = unpack("V*", $s. str_repeat("\0", (4 - strlen($s) % 4) & 3));
$v = array_values($v);
if ($w) {
$v[count($v)] = strlen($s);
}
return $v;
}
function int32($n) {
while ($n >= 2147483648) $n -= 4294967296;
while ($n <= -2147483649) $n += 4294967296;
return (int)$n;
}
function xxtea_encrypt($str, $key) {
if ($str == "") {
return "";
}
$v = str2long($str, true);
$k = str2long($key, false);
if (count($k) < 4) {
for ($i = count($k); $i < 4; $i++) {
$k[$i] = 0;
}
}
$n = count($v) - 1;
$z = $v[$n];
$y = $v[0];
$delta = 0x9E3779B9;
$q = floor(6 + 52 / ($n + 1));
$sum = 0;
while (0 < $q--) {
$sum = int32($sum + $delta);
$e = $sum >> 2 & 3;
for ($p = 0; $p < $n; $p++) {
$y = $v[$p + 1];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$z = $v[$p] = int32($v[$p] + $mx);
}
$y = $v[0];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$z = $v[$n] = int32($v[$n] + $mx);
}
return long2str($v, false);
}
function xxtea_decrypt($str, $key) {
if ($str == "") {
return "";
}
$v = str2long($str, false);
$k = str2long($key, false);
if (count($k) < 4) {
for ($i = count($k); $i < 4; $i++) {
$k[$i] = 0;
}
}
$n = count($v) - 1;
$z = $v[$n];
$y = $v[0];
$delta = 0x9E3779B9;
$q = floor(6 + 52 / ($n + 1));
$sum = int32($q * $delta);
while ($sum != 0) {
$e = $sum >> 2 & 3;
for ($p = $n; $p > 0; $p--) {
$z = $v[$p - 1];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$y = $v[$p] = int32($v[$p] - $mx);
}
$z = $v[$n];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$y = $v[0] = int32($v[0] - $mx);
$sum = int32($sum - $delta);
}
return long2str($v, true);
}
?>
Пример шифрования/дешифрования
В представленном коде мы видим, что у нас есть в распоряжении две функции: xxtea_encrypt и xxtea_decrypt. Остальные функции носят вспомогательный характер. Конечно, можно было бы эту реализацию вынести в отдельный класс, но оставим все как есть.
Пробуем зашифровать и сразу расшифровать текстовую строку:
require_once 'xxtea.inc.php';
$cipher = xxtea_encrypt('My test string', 'My test password');
echo xxtea_decrypt($cipher, 'My test password');
При выводе получаем: My test string
Стоит нам изменить ключ при расшифровке, при выводе мы не получим ничего, т.к. ключ задан неверно. Длина ключа может быть не более 128 бит, т.е. 16 байт. Поэтому если мы зададим в качестве второго аргумента функции xxtea_encrypt/xxtea_decrypt значение длиной больше 16 байт, она примет во внимание только первые 16 байт. Поэтому здесь нужно быть внимательным.
Таким же образом с легкостью можно шифровать содержимое файла.
Секретный ключ всегда следует хранить отдельно от шифра, иначе весь смысл шифрования конечно теряется. На то он и секретный, что его должен знать только тот, кто зашифровал, чтобы впоследствии обратить шифр.
В следующих статьях мы также затронем тему шифрования – не менее интересные возможности языка PHP и его расширений.
Скачать пример к статье: xxtea.zip (1,21 Кб)
Январь 9th, 2009
Спасибо, интересно почитать было, только чем вас TEA именно впечатлил?
> Стоит нам изменить ключ при расшифровке, при
> выводе мы не получим ничего, т.к. ключ задан неверно
хм… ничего вообще или мусор?
Январь 9th, 2009
Про такие алгоритмы, как RC2, RC4 и т.д. все давно наслышаны. А TEA – малоизвестен, поэтому про него написал. Да и легкий в реализации. Насчет расшифровки: у меня при задании неправильного ключа не выводилось ничего. Видимо одна из вспомогательных функций работает так, что возвращает false, если что-то не срастается при расшифровке.
Январь 10th, 2009
Да, к сожалению на php эффективную реализацию сделать достаточно трудно. На C/Delphi есть указатели и поэтому не надо постоянно возиться с алгоритмами преобразования типов. А тут всё по другому…

Кстати,
> Асимметричные шифры обычно работают медленнее.
Тут бы я добавил про гибридные алгоритмы пару слов.
Но всё очень круто и исходничек пригодиться обязательно.
Спасибо!
Январь 15th, 2009
У PEAR тоже есть свой вариант подобного шифрования (в виде класса)…
Интересно мне, возможно ли зашифрованную таким методом строку отправлять, к примеру в cookie пользователя? Как браузер отнесётся к этому? Получаются, ведь бинарные данные. И, другой вопрос, если из куков потом забрать такую строку, будет ли она такой же, ведь кодировки сайта и пользователя могут быть разными?
Январь 15th, 2009
Попробуйте
Если не получится расшифровать из-за искажения шифра – посоветую перед сохранением в cookie кодировать шифр в base64, а при чтении из cookie – раскодировать.
Январь 19th, 2009
Спасибо, надо поэкспериментировать!
Февраль 14th, 2009
Хочу предложить еще один простой способ шифрования.
В основу заложен давно известный алгоритм: Если выполнить XOR данных
и ключа, то при повторном выполнении XOR шифрованной строки с тем же
(и только!) ключом будет восстановлена исходная строка.
К преимуществам можно отнести простоту реализации
(за счет того, что основная доля вычислений осуществляется встроенной
функцией md5), а также то, что для шифрования и дешифрования
используется одна и та же функция.
Шифрованная строка получается бинарной, так что при необходимости
нужно использовать base64
$klen){$key=str_pad($key,$slen,$key,STR_PAD_RIGHT);}
if($slen
Июнь 16th, 2009
Я тоже использую XOR, и всегда хотел спросить: насколько вообще это надежный способ шифрования?
Июнь 16th, 2009
Boris, если бы XOR был надежен, люди не придумывали бы столько алгоритмов шифрования, существующих на сегодняшний день. Хотя может быть все зависит от длины ключа и я могу ошибаться.
Июль 8th, 2009
А как таким алгаритмом закодировать файл скажем в 100 мегобайт (дамб базы в текстовом файле), если нельзя увиличить размер оперативки выделяемой пхп (по дефалту 32м)?
Июль 8th, 2009
В таком случае можно шифровать файл поблочно. Например, блоками размером в 1 Мб. Существуют разные режимы шифрования. Поблочный, если не ошибаюсь, называется CBC.
Июль 8th, 2009
novice, а по подробнее где можно найти? желательно с примерами
Июль 8th, 2009
Июль 9th, 2009
Сомневаюсь, что файл 100м вообще можно зашифровать на php,
30-секундный лимит не позволит, а менять его на хостинге вряд ли разрешат. Тем более, что приведенный метод довольно медленный, ладно бы через библиотеки типа mcrypt
Октябрь 26th, 2009
Не знаю прочитает ли мой коммент кто-нибудь, но все равно напишу.
Вся проблема XOR в том, что если сделать XOR к двум зашифрованным строчкам, что мы получим?
Правильно! Мы получим ключ.
Достаточно перехватить два пакета, письма, cookie и у нас будет ключ.
Март 17th, 2010
Если для шифрования использовать “My test password”, то для дешифрования подходит любой, который начинается с этой фразы. к примеру идеально подходят “My test passworddddd”, “My test passwordasdfk” и т.д. Абалдеть какая защита. Впрочем, чему удивляться, у китайцев мало надежных вещей. Ищите люди в японии
Июль 23rd, 2010
Сергей, вы внимательно читали текст статьи? Судя по всему невнимательно. Автор ясно и четко написал в конце статьи: “Длина ключа может быть не более 128 бит, т.е. 16 байт. Поэтому если мы зададим в качестве второго аргумента функции xxtea_encrypt/xxtea_decrypt значение длиной больше 16 байт, она примет во внимание только первые 16 байт” Длинна пароля “My test password” как раз и составляет 16 байт (пробелы тоже учитываются, один символ кодируется одним байтом, байт равен восьми битам), поэтому те буквы которые вы добавляете и меняете уже не играют никакой роли. Ключ длинной в 16 байт (128 бит) достаточно надежен, если кому то нужен ключ большей длинны можно использовать другой алгоритм. Лично я выражаю автору благодарность за статью, и готовый код который пригодится.