Я здивований тим, що здається примхою .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 правило полягає в тому, щоб позначити будь-які аномалії, а помилкове заповнення на (очевидному) кінці потоку є те, що документація розповість вам про виняток.