.NET CryptoStream читает последний конец зашифрованного текста в Dispose () и взрывается - PullRequest
3 голосов
/ 14 сентября 2011

Я озадачен тем, что кажется причудой к классу .NET CryptoStream: его метод Dispose() читает после конца зашифрованного текста в поисках отступа, который не должен, и выдает CryprographicException в результате.

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

  • Зашифрованный текст составляет 8 байтов, один блок шифрования 3DES. Поскольку я записываю только 6 байтов в CryptoStream и он использует PaddingMode.PKCS7 (по умолчанию), оставшиеся два байта в блоке заполняются значением заполнения 0x02.
  • Размер массива зашифрованного текста впоследствии изменяется до 16 байтов, двух блоков 3DES. Второй блок - неинициализированная ерунда, недействительный вывод шифра.
  • При расшифровке я читаю ровно 6 байтов из CryptoStream; Я не прошу его расшифровать в бессмысленную часть, и я не полагаюсь на то, что он распознает отступ, чтобы выяснить, когда он достигнет конца открытого текста.

Проблема в том, что когда вызывается расшифровка CryptoStream Dispose() (автоматически в конце блока using), я получаю CryptographicException с сообщением «Неверные данные». Его трассировка стека показывает, что он выполнял CryptoStream.FlushFinalBlock(), и все 16 байтов были использованы из ciphertextStream, а не только 8, соответствующих фактическим зашифрованным данным.

Если я удаляю строку, которая изменяет размер массива ciphertext, программа работает правильно. И если я сделаю tripleDes.Padding = PaddingMode.None перед расшифровкой, программа также будет работать правильно - но это в основном делает байты заполнения частью открытого текста, поэтому я бы предпочел этого не делать. Ясно, что проблема связана с дополнением; насколько я могу судить, он расшифровывает этот второй блок и ожидает, что в его конце будет найден допустимый отступ в стиле PKCS7.

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


using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] plaintext = { 0, 1, 2, 3, 4 };

            using (SymmetricAlgorithm tripleDes = TripleDESCryptoServiceProvider.Create())
            {
                // Encrypt the plaintext
                byte[] ciphertext;
                using (MemoryStream ciphertextStream = new MemoryStream())
                {
                    using (ICryptoTransform encryptor = tripleDes.CreateEncryptor())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.WriteByte((byte)plaintext.Length);
                            cryptoStream.Write(plaintext, 0, plaintext.Length);
                            cryptoStream.FlushFinalBlock();
                        }
                    }

                    ciphertext = ciphertextStream.ToArray();
                }

                // *** Add some non-ciphertext garbage to the end ***
                Array.Resize(ref ciphertext, ciphertext.Length + 8);

                // Now decrypt it again
                byte[] decryptedPlaintext;
                using (MemoryStream ciphertextStream = new MemoryStream(ciphertext, false))
                {
                    using (ICryptoTransform decryptor = tripleDes.CreateDecryptor())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, decryptor, CryptoStreamMode.Read))
                        {
                            int length = cryptoStream.ReadByte();

                            decryptedPlaintext = new byte[length];

                            int i = 0;
                            while (i < length)
                            {
                                int bytesRead = cryptoStream.Read(decryptedPlaintext, i, (length - i));
                                if (bytesRead == 0) break;
                                else i += bytesRead;
                            }
                        }  // CryptographicException: "Bad Data"
                    }
                }

                System.Diagnostics.Debug.Assert(decryptedPlaintext.SequenceEqual(plaintext));
            }
        }
    }
}

1 Ответ

3 голосов
/ 14 сентября 2011

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

В криптографии все должно быть очень тщательно проверено, чтобы убедиться, чтоЗлоумышленник не пытается что-то подлое.Если вы укажете заполнение PKCS7, тогда поток вправе проверить заполнение PKCS7 в конце, а право выбросить исключение, если он не найдет правильное заполнение в конце потока.

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

...