Почему неверный пароль приводит к тому, что «Padding недействителен и не может быть удален»? - PullRequest
40 голосов
/ 15 августа 2008

Мне понадобилось простое шифрование строк, поэтому я написал следующий код (с большим «вдохновением» из здесь ):

    // create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

Код работает нормально, за исключением того, что при расшифровке данных с использованием неверного ключа я получаю исключение CryptographicException - «Заполнение недопустимо и его нельзя удалить» - в строке cs.Close () в decryptString.

пример кода:

    string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

Мой вопрос такой: чего ожидать? Я бы подумал, что расшифровка с неправильным паролем приведет к бессмысленному выводу, а не к исключению.

Ответы [ 9 ]

27 голосов
/ 25 августа 2008

Хотя на этот вопрос уже дан ответ, я думаю, что было бы неплохо объяснить , почему этого следует ожидать.

Обычно применяется схема заполнения, поскольку большинство криптографических фильтров не являются семантически безопасными и предотвращают некоторые формы криптоатак. Например, обычно в RSA используется схема заполнения OAEP , которая предотвращает некоторые виды атак (такие как выбранная атака открытым текстом или ослепление ).

Схема заполнения добавляет некоторый (обычно) случайный мусор к сообщению m перед отправкой сообщения. В методе OAEP, например, используются два оракула (это упрощенное объяснение):

  1. Учитывая размер модуля, вы добавляете k1 бит с 0 и k0 бит со случайным числом.
  2. Затем, применив некоторое преобразование к сообщению, вы получите заполненное сообщение, которое зашифровано и отправлено.

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

17 голосов
/ 07 ноября 2013

У меня возникло похожее «Заполнение недействительно и не может быть удалено». исключение, но в моем случае клавиша IV и отступ были правильными.

Оказалось, что очистка криптопотока - это все, чего не хватало.

Как это:

            MemoryStream msr3 = new MemoryStream();
            CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write);
            encStream.Write(bar2, 0, bar2.Length);
            // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding
            encStream.FlushFinalBlock();
            byte[] bar3 = msr3.ToArray();
5 голосов
/ 20 февраля 2013

Если вы хотите, чтобы ваше использование было правильным, вы должны добавить аутентификацию к вашему зашифрованному тексту, чтобы вы могли убедиться, что это правильный пароль или что зашифрованный текст не был изменен. Заполнение, которое вы используете ISO10126 , вызовет исключение, только если последний байт не расшифрован как одно из 16 допустимых значений для заполнения (0x01-0x10). Таким образом, у вас есть шанс 1/16, что он НЕ сгенерирует исключение с неправильным паролем, где, если вы аутентифицируете его, у вас есть детерминированный способ определить, правильна ли ваша расшифровка.

Использование crypto api, хотя, на первый взгляд, легко, на самом деле довольно легко совершать ошибки. Например, вы используете фиксированную соль для получения ключа и IV, что означает, что каждый зашифрованный текст, зашифрованный тем же паролем, будет повторно использовать свой IV с этим ключом, что нарушает семантическую безопасность в режиме CBC, IV должен быть непредсказуемым и уникальным данный ключ.

По этой причине легко допустить ошибки, у меня есть фрагмент кода, который я стараюсь постоянно проверять и обновлять (комментарии, вопросы приветствуются):

Современные примеры симметричного аутентифицированного шифрования строки C #.

Если вы используете AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) при использовании неверного пароля, возвращается null, если зашифрованный текст или iv был изменен после шифрования null возвращается, вы никогда не получите обратно ненужные данные или заполнение исключение.

4 голосов
/ 13 октября 2016

Если вы исключили несоответствие клавиш, то, кроме FlushFinalBlock() (см. Ответ Янива), достаточно будет также вызвать Close() на CryptoStream.

Если вы очищаете ресурсы строго с помощью блоков using, обязательно вложите блок для самого CryptoStream:

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
{
  using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
  {
    encStream.Write(bar2, 0, bar2.Length);
  } // implicit close
  byte[] encArray = ms.ToArray();
}

Я был укушен этим (или похожим):

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
{
  encStream.Write(bar2, 0, bar2.Length);
  byte[] encArray = ms.ToArray();
} // implicit close -- too late!
3 голосов
/ 15 августа 2008

Да, этого и следовало ожидать, или, по крайней мере, именно так и происходит, когда наши крипто-подпрограммы получают нешифруемые данные

2 голосов
/ 10 октября 2016

Другой причиной исключения может быть состояние состязания между несколькими потоками, использующими логику дешифрования - нативные реализации ICryptoTransform не безопасны для потоков (например, SymmetricAlgorithm), поэтому его следует поместить в эксклюзивный раздел, например, используя lock . Пожалуйста, обратитесь сюда для получения более подробной информации: http://www.make -awesome.com / 2011/07 / system-security-cryptography-and-thread-safety /

1 голос
/ 04 августа 2010

В CryptoStream могут быть некоторые непрочитанные байты. Закрытие перед полным чтением потока вызывало ошибку в моей программе.

0 голосов
/ 15 апреля 2015

Ответ, обновленный пользователем "atconway", сработал для меня.

Проблема была не в заполнении, а в ключе, который отличался при шифровании и дешифровании. Ключ и iv должны быть одинаковыми во время шифрования и дешифрования одного и того же значения.

0 голосов
/ 02 февраля 2015

У меня была похожая проблема, проблема в методе расшифровки заключалась в инициализации пустого потока памяти. когда он работал, когда я инициализировал его с помощью байтового массива зашифрованного текста, например:

MemoryStream ms = new MemoryStream(cipherText)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...