/ / .NET CryptoStream читає минулий кінець шифрувального тексту в Dispose () і вибухає - .net, шифрування, оббивка, cryptostream

.NET CryptoStream читає минулий кінець шифру тексту в Dispose () і вибухне - .net, шифрування, заповнення, криптострим

Я здивований тим, що здається примхою .NET CryptoStream клас: його Dispose() Метод читає повз кінця зашифрованого тексту, шукаючи оббивку, що він не повинен, і кидає a CryprographicException як результат.

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

  • Зашифрований текст 8 байтів, один блок шифрування 3DES. Так як я тільки пишу 6 байт в CryptoStream і він використовує PaddingMode.PKCS7 (за замовчуванням), інші два байти в блоці заповнені значенням заповнення 0x02.
  • Масив шифрувального тексту згодом змінюється на 16 байт, два блоку 3DES. Другий блок є неініціалізованим нонсенсом, не дійсним виведенням шифру.
  • При розшифровці я читаю точно 6 байт з CryptoStream; I "m ні просячи його розшифрувати в безглуздість, а я "m" ні покладаючись на це, розпізнаючи прокладку, щоб з'ясувати, коли він досяг кінця відкритого тексту.

Проблема в тому, що при розшифровці 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));
}
}
}
}

Відповіді:

3 для відповіді № 1

Ви навмисно додаєте сміття до кінця потоку, а потім задаєтеся питанням, чому потік задихається на сміття.

У криптографії все необхідно перевірити дуже обережно, щоб забезпечити цезловмисник не намагається щось підлий. Якщо вказати параметр PKCS7, то потік має право перевірити наявність заповнення PKCS7 в кінці і право вилучити виняток, якщо він не знаходить правильного заповнення в кінці потоку.

Потік не має можливості дізнатися, що самекіпертекст закінчується посередині потоку, а не в кінці. Як ви очікуєте, що це знатиме? У crypto правило полягає в тому, щоб позначити будь-які аномалії, а помилкове заповнення на (очевидному) кінці потоку є те, що документація розповість вам про виняток.