Подтвердить что ты не робот

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

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

Любые идеи?

4b9b3361

Ответ 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.)

Ответ 2

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

Ответ 3

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

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