Как добавить / удалить заполнение PKCS7 из зашифрованной строки AES? - PullRequest
17 голосов
/ 06 сентября 2011

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

Есть идеи?

Ответы [ 3 ]

56 голосов
/ 06 сентября 2011

Посмотрим.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) в качестве *Функция 1087 * в PHP имеет специальную обработку для отрицательных операндов / аргументов, чтобы отсчитывать от конца строки.(Я не знаю, является ли это более или менее эффективным, чем сначала получить длину и вычислить индекс вручную.)

Как было сказано выше и отмечено в комментарии Россумом, вместо простого удаления отступовкак сделано здесь, вы должны проверить, что это правильно - то есть посмотрите на substr($str, $len - $pad) и убедитесь, что все его байты chr($pad).Это служит небольшой проверкой на предмет коррупции (хотя эта проверка более эффективна, если вы используете режим цепочки вместо ECB, и не является заменой для реального MAC).


(И все же, скажитеВашему клиенту они должны подумать о переходе на более безопасный режим, чем ECB.)

8 голосов
/ 21 декабря 2014

Я создал два метода для заполнения и разгрузки.Функции документированы с использованием 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 или защищенного аутентифицированного шифра).

0 голосов
/ 22 декабря 2017

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

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