Как добавить / удалить дополнение PKCS7 из зашифрованной строки AES?

Я пытаюсь шифровать / дешифровать строку с использованием 128-битного AES-шифрования (ECB). Я хочу знать, как я могу добавить / удалить дополнение PKCS7 к нему. Похоже, что расширение Mcrypt может позаботиться о шифровании / расшифровке, но дополнение должно быть добавлено / удалено вручную.

Есть идеи?

Solutions Collecting From Web of "Как добавить / удалить дополнение PKCS7 из зашифрованной строки AES?"

Посмотрим. 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 times, т. е. строка длины $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.)

Я создал два метода для заполнения и распаковки. Функции документируются с использованием phpdoc и требуют PHP 5. Как вы заметите, функция unpad содержит много обработки исключений, генерируя не менее 4 разных сообщений для каждой возможной ошибки.

Чтобы получить размер блока для PHP mcrypt, вы можете использовать mcrypt_get_block_size , который также определяет размер блока в байтах вместо битов.

 /** * Right-pads the data string with 1 to n bytes according to PKCS#7, * where n is the block size. * The size of the result is x times n, where x is at least 1. * * The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. * This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. * * @param string $plaintext the plaintext encoded as a string containing bytes * @param integer $blocksize the block size of the cipher in bytes * @return string the padded plaintext */ function pkcs7pad($plaintext, $blocksize) { $padsize = $blocksize - (strlen($plaintext) % $blocksize); return $plaintext . str_repeat(chr($padsize), $padsize); } /** * Validates and unpads the padded plaintext according to PKCS#7. * The resulting plaintext will be 1 to n bytes smaller depending on the amount of padding, * where n is the block size. * * The user is required to make sure that plaintext and padding oracles do not apply, * for instance by providing integrity and authenticity to the IV and ciphertext using a HMAC. * * Note that errors during uppadding may occur if the integrity of the ciphertext * is not validated or if the key is incorrect. A wrong key, IV or ciphertext may all * lead to errors within this method. * * The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. * This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. * * @param string padded the padded plaintext encoded as a string containing bytes * @param integer $blocksize the block size of the cipher in bytes * @return string the unpadded plaintext * @throws Exception if the unpadding failed */ function pkcs7unpad($padded, $blocksize) { $l = strlen($padded); if ($l % $blocksize != 0) { throw new Exception("Padded plaintext cannot be divided by the block size"); } $padsize = ord($padded[$l - 1]); if ($padsize === 0) { throw new Exception("Zero padding found instead of PKCS#7 padding"); } if ($padsize > $blocksize) { throw new Exception("Incorrect amount of PKCS#7 padding for blocksize"); } // check the correctness of the padding bytes by counting the occurance $padding = substr($padded, -1 * $padsize); if (substr_count($padding, chr($padsize)) != $padsize) { throw new Exception("Invalid PKCS#7 padding encountered"); } return substr($padded, 0, $l - $padsize); } 

Это никоим образом не аннулирует ответ Paŭlo Ebermann, это в основном тот же самый ответ в коде и phpdoc, а не как описание.


Обратите внимание, что возврат ошибки дополнения к злоумышленнику может привести к атаке оскорбления, которая полностью нарушает CBC (когда CBC используется вместо ECB или защищенного аутентифицированного шифра).

Просто вызывается следующая функция после дешифрования данных

 function removePadding($decryptedText){ $strPad = ord($decryptedText[strlen($decryptedText)-1]); $decryptedText= substr($decryptedText, 0, -$strPad); return $decryptedText; }