Заполнение недопустимо и не может быть удалено?


Я искал в интернете, что означает это исключение в отношении моей программы, но не могу найти решение или причину, по которой это происходит с моей конкретной программой. Я использовал пример, предоставленный моим msdn для шифрования и расшифровки XmlDocument с помощью алгоритма Rijndael. Шифрование работает нормально, но когда я пытаюсь расшифровать, я получаю следующее исключение:

заполнение недопустимо и не может быть удалено

может кто-нибудь сказать что я могу сделать, чтобы решить эту проблему? Мой код ниже, где я получаю ключ и другие данные. Если cryptoMode имеет значение false, он вызовет метод decrypt, в котором происходит исключение:

public void Cryptography(XmlDocument doc, bool cryptographyMode)
{
    RijndaelManaged key = null;
    try
    {
    // Create a new Rijndael key.
    key = new RijndaelManaged();
    const string passwordBytes = "Password1234"; //password here 

    byte[] saltBytes = Encoding.UTF8.GetBytes("SaltBytes");
    Rfc2898DeriveBytes p = new Rfc2898DeriveBytes(passwordBytes, saltBytes);
    // sizes are devided by 8 because [ 1 byte = 8 bits ] 
    key.IV = p.GetBytes(key.BlockSize/8);
    key.Key = p.GetBytes(key.KeySize/8);

    if (cryptographyMode)
    {
        Ecrypt(doc, "Content", key);
    }
    else
    {
        Decrypt(doc, key);
    }

    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.Message);
    }
    finally
    {
    // Clear the key.
    if (key != null)
    {
        key.Clear();
    }
    }

}

private void Decrypt(XmlDocument doc, SymmetricAlgorithm alg)
{
    // Check the arguments.  
    if (doc == null)
    throw new ArgumentNullException("Doc");
    if (alg == null)
    throw new ArgumentNullException("alg");

    // Find the EncryptedData element in the XmlDocument.
    XmlElement encryptedElement = doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;

    // If the EncryptedData element was not found, throw an exception.
    if (encryptedElement == null)
    {
    throw new XmlException("The EncryptedData element was not found.");
    }


    // Create an EncryptedData object and populate it.
    EncryptedData edElement = new EncryptedData();
    edElement.LoadXml(encryptedElement);

    // Create a new EncryptedXml object.
    EncryptedXml exml = new EncryptedXml();


    // Decrypt the element using the symmetric key.
    byte[] rgbOutput = exml.DecryptData(edElement, alg); <----  I GET THE EXCEPTION HERE
    // Replace the encryptedData element with the plaintext XML element.
    exml.ReplaceData(encryptedElement, rgbOutput);

}
11 80

11 ответов:

Rijndael/AES-это блочный шифр. Он шифрует данные в 128 битных (16 символьных) блоках. криптографические атрибуты используется, чтобы убедиться, что последний блок сообщения-это всегда правильный размер.

ваш метод расшифровки ожидает независимо от его заполнения по умолчанию и не находит его. Как говорит @NetSquirrel, вам нужно явно установить заполнение как для шифрования, так и для расшифровки. Если у вас нет причин делать иначе, используйте PKCS#7 padding.

убедитесь, что ключи, которые вы используете для шифровать и расшифровать are тот же. Метод заполнения, даже если он не задан явно, должен по-прежнему обеспечивать правильную расшифровку/шифрование (если они не заданы, они будут одинаковыми). Однако если вы по какой-то причине используете другой набор ключей для расшифровки, чем используемый для шифрования вы будет получаю ошибку:

заполнение недопустимо и не может быть удалено

Если вы используете какой-то алгоритм для динамического генерирования ключей, которые не будут работать. Они должны быть одинаковыми для шифрования и дешифрования. Один из распространенных способов заключается в том, чтобы вызывающий объект предоставлял ключи в конструкторе класса методов шифрования, чтобы предотвратить процесс шифрования/дешифрования, имеющий какую-либо руку в создании этих элементов. Он фокусируется на задаче под рукой (шифрование и расшифровка данных) и требует iv и key быть поставлено абонент.

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

Это может относиться к ответу Россума, но подумал, что стоит упомянуть.

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

  1. изменить класс алгоритм. Замените RijndaelManaged класс AESManaged один.
  2. Не устанавливайте явно KeySize класса алгоритма, оставил их по умолчанию.
    (Это очень важный шаг. Я думаю, что есть ошибка в свойстве ключа.)

вот список вы хотите проверить, какой аргумент вы могли пропустить:

  • ключ
    (массив байтов, длина должна быть точно одна из 16, 24, 32 байт для различного размера ключа.)
  • IV
    (массив байт, 16 байт)
  • CipherMode
    (Один из CBC, CFB, CTS, ЕЦБ, OFB)
  • PaddingMode
    (Один из ANSIX923, ISO10126, нет, PKCS7, нули)

Если один и тот же ключ и вектор инициализации используются для кодирования и декодирования, эта проблема возникает не из декодирования данных, а из кодирования данных.

после того, как Вы называют метод записи на объект cryptostream, вы всегда должны вызвать метод FlushFinalBlock до закрытия метода.

документация MSDN по CryptoStream.Метод FlushFinalBlock говорит:
"вызов метода Close вызовет FlushFinalBlock ..."
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs. 110).aspx
Это неправильно. Вызов метода Close просто закрывает CryptoStream и выходной поток.
Если вы не позвоните FlushFinalBlock до закрытия после того, как вы написали, данные должны быть зашифрованы, для расшифровки данных, вызов на чтение или копирование, чтобы метод на поток cryptostream объект поднимет исключение CryptographicException (сообщение: "обивка является недействительным и не может быть удален").

Это, вероятно, верно для всех алгоритмов шифрования, полученных из SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES), хотя я только что проверил это для AesManaged и MemoryStream в качестве выходного потока.

Итак, если вы получаете это исключение CryptographicException при расшифровке, прочитайте значение свойства длины выходного потока после того, как вы написали свои данные для шифрования, затем вызовите FlushFinalBlock и снова прочитайте его значение. Если он изменился, вы знаете, что вызов FlushFinalBlock не является необязательным.

и вам не нужно выполнять какое-либо заполнение программно или выбирать другое значение свойства заполнения. Заполнение-это задание метода FlushFinalBlock.

.........

дополнительное замечание для Кевина:

да, CryptoStream вызывает FlushFinalBlock перед вызовом Close, но слишком поздно: когда вызывается метод Cryptostream Close, выходной поток также закрывается.

Если ваш выходной поток-это поток памяти, вы не можете прочитать его данные после его закрытия. Поэтому вам нужно вызвать FlushFinalBlock на вашем CryptoStream перед использованием зашифрованных данных, записанных в MemoryStream.

Если ваш выходной поток является потоком файлов, все хуже, потому что запись буферизуется. Следствием этого является то, что последние записанные байты не могут быть записаны в файл, если вы закрываете выходной поток перед вызовом Flush on FileStream. Поэтому перед вызовом Close на CryptoStream вам сначала нужно позвонить FlushFinalBlock на ваш поток cryptostream потом вызывать flush на вашем файлового потока.

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

другой сценарий, опять же в интересах поиска людей.

для меня эта ошибка произошла во время метода Dispose (), который маскировал предыдущую ошибку, не связанную с шифрованием.

Как только другой компонент был исправлен, это исключение исчезло.

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

решение для меня было место

        try
            decryption stuff....
        catch
             inform decryption will not be carried out.
        end try

Как я уже сказал, моя ошибка заполнения заключалась в том, что я вручную набирал расшифрованный текст с помощью блокнота. Может быть, мой ответ может привести вас к вашему решению.

решение, которое исправило мое, состояло в том, что я непреднамеренно применил разные ключи к методам шифрования и дешифрования.

У меня была такая же ошибка. В моем случае это было потому, что я сохранил зашифрованные данные в базе данных SQL. Таблица, в которой хранятся данные, имеет двоичный(1000) тип данных. При восстановлении данных из базы данных, он будет расшифровывать эти 1000 байт, в то время как там, где на самом деле 400 байт. Таким образом, удаление замыкающего нуля (600) из результата исправило проблему.

я столкнулся с этой ошибкой при попытке передать незашифрованный путь к файлу в метод Decrypt.Решение состояло в том, чтобы проверить, зашифрован ли переданный файл, прежде чем пытаться расшифровать

if (Sec.IsFileEncrypted(e.File.FullName))
{
    var stream = Sec.Decrypt(e.File.FullName);
} 
else
{
    // non-encrypted scenario  
}