Ответ 1
Посмотрим. PKCS # 7 описан в RFC 5652 (синтаксис криптографического сообщения).
Схема прокладки сама по себе приведена в разделе 6.3. Процесс шифрования контента. По существу, он говорит: добавьте столько байтов, сколько необходимо, чтобы заполнить заданный размер блока (но, по крайней мере, один), и каждый из них должен иметь длину заполнения как значение.
Таким образом, глядя на последний дешифрованный байт, мы знаем, сколько байтов нужно снять. (Можно также проверить, что все они имеют одинаковое значение.)
Теперь я могу дать вам пару функций PHP, но мой PHP немного ржавый. Так что сделайте это самостоятельно (тогда не стесняйтесь редактировать мой ответ, чтобы добавить его), или посмотрите на примечания, внесенные пользователем, на mcrypt - некоторые из них относятся к дополнению и обеспечивают реализацию дополнений PKCS # 7.
Итак, рассмотрим первую заметку там:
<?php
function encrypt($str, $key)
{
$block = mcrypt_get_block_size('des', 'ecb');
Это получает размер блока используемого алгоритма. В вашем случае вы использовали бы aes
или rijndael_128
вместо des
, я полагаю (я его не тестировал). (Вместо этого вы можете просто взять 16
здесь для AES вместо вызова функции.)
$pad = $block - (strlen($str) % $block);
Здесь вычисляется размер заполнения. strlen($str)
- это длина ваших данных (в байтах), % $block
дает остаток по модулю $block
, то есть количество байтов данных в последнем блоке. $block - ...
, таким образом, дает количество байтов, необходимых для заполнения этого последнего блока (теперь это число между 1
и $block
, включительно).
$str .= str_repeat(chr($pad), $pad);
str_repeat
создает строку, состоящую из повторения одной и той же строки, здесь повторение символ, заданный $pad
, $pad
раз, т.е. строка длиной $pad
, заполненная $pad
.
$str .= ...
добавляет эту строку дополнения к исходным данным.
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
Вот шифрование. Используйте MCRYPT_RIJNDAEL_128
вместо MCRYPT_DES
.
}
Теперь другое направление:
function decrypt($str, $key)
{
$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
Расшифровка. (Вы, конечно, измените алгоритм, как указано выше). $str - это расшифрованная строка, включая дополнение.
$block = mcrypt_get_block_size('des', 'ecb');
Это снова размер блока. (См. Выше.)
$pad = ord($str[($len = strlen($str)) - 1]);
Это выглядит немного странно. Лучше напишите его несколькими шагами:
$len = strlen($str);
$pad = ord($str[$len-1]);
$len
теперь является длиной заполненной строки, а $str[$len - 1]
является последним символом этой строки. ord
преобразует это в число. Таким образом, $pad
- это число, которое мы ранее использовали в качестве значения заполнения для заполнения, и это длина заполнения.
return substr($str, 0, strlen($str) - $pad);
Итак, теперь мы отсекаем последние $pad
байты из строки. (Вместо strlen($str)
мы могли бы также написать $len
здесь: substr($str, 0, $len - $pad)
.).
}
?>
Обратите внимание, что вместо использования substr($str, $len - $pad)
можно также написать substr($str, -$pad)
, так как функция substr
в PHP имеет специальную обработку для отрицательных операндов/аргументов для подсчета с конца строки. (Я не знаю, если это более или менее эффективно, чем получение первой длины и вычисление индекса вручную.)
Как сказано ранее и отмечено в комментарии от rossum, вместо простого удаления отступов, как это сделано здесь, вы должны проверить, что это правильно - например, посмотреть substr($str, $len - $pad)
и проверить, что все его байты chr($pad)
, Это служит небольшой проверкой против коррупции (хотя эта проверка более эффективна, если вы используете режим цепочки вместо ECB и не являетесь заменой реального MAC).
(И все же скажите своему клиенту, что им следует подумать о переходе в более безопасный режим, чем в ECB.)