/ / .NET CryptoStream liest das letzte Ende des Chiffretextes in Dispose () ein und bläst - .net, Verschlüsselung, Auffüllen, Cryptostream

.NET CryptoStream liest das letzte Ende des Chiffretextes in Dispose () und sprengt - .net, encryption, padding, cryptostream

Ich bin verblüfft über das, was scheinbar eine Eigenart des .NET ist CryptoStream Klasse: seine Dispose() Die Methode liest über das Ende des Chiffretextes hinaus und sucht nach Füllzeichen, die es nicht sein sollte, und wirft ein CryprographicException als Ergebnis.

Das unten stehende C # -Programm verschlüsselt einige Bytes,Ändert die Größe des Chiffretext-Arrays so, dass nach dem Ende des Chiffretextes mehr (unsinnige) Bytes vorhanden sind, und versucht dann, ihn zu entschlüsseln. Die hervorstechenden Punkte sind:

  • Der Chiffretext besteht aus 8 Bytes, einem 3DES-Chiffrierblock. Da schreibe ich nur 6 Bytes in die CryptoStream und es verwendet PaddingMode.PKCS7 (Standardeinstellung), werden die restlichen zwei Bytes im Block mit dem Füllwert 0x02 gefüllt
  • Das Chiffretext-Array wird anschließend auf 16 Bytes, zwei 3DES-Blöcke, verkleinert. Der zweite Block ist nicht initialisierter Unsinn, keine gültige Verschlüsselungsausgabe.
  • Beim Entschlüsseln lese ich genau 6 Bytes aus CryptoStream; Ich bin nicht bittet ihn, in den Unsinnbereich zu entschlüsseln, und ich bin nicht sich darauf verlassen, dass er die Polsterung erkennt, um herauszufinden, wann das Ende des Klartextes erreicht ist.

Das Problem ist das beim Entschlüsseln CryptoStream"s Dispose() wird aufgerufen (automatisch am Ende des using block), ich bekomme eine CryptographicException mit der Meldung "Bad Data". Sein Stack-Trace zeigt, dass es ausgeführt wurde CryptoStream.FlushFinalBlock()und alle 16 Bytes wurden aus der Datenbank verbraucht ciphertextStreamnicht nur die 8, die den tatsächlich verschlüsselten Daten entsprechen.

Wenn ich die Zeile entferne, die die Größe ändert ciphertext Array funktioniert das Programm korrekt. Und wenn ich das tue tripleDes.Padding = PaddingMode.None Vor dem Entschlüsseln funktioniert das Programm auchrichtig - aber das macht die Auffüll-Bytes im Grunde zum Klartext, daher mache ich das lieber nicht. Offensichtlich hängt das Problem mit dem Padding zusammen. Soweit ich das beurteilen kann, entschlüsselt er den zweiten Block und erwartet dies Gültig finden PKCS7-Style-Polsterung am Ende.

Da lese ich nur genug vom CryptoStream um zu verlangen, dass ein Block entschlüsselt wird, und dieser Block ist ein korrekt aufgefüllter abschließender Block, und dann schließe ich den Block CryptoStream Warum muss der Stream ohne Lesen noch einen weiteren Block lesen und nach mehr Auffüllung suchen? Warum versucht es sogar, mehr Input als Teil seiner zu verbrauchen 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));
}
}
}
}

Antworten:

3 für die Antwort № 1

Sie fügen absichtlich Müll am Ende des Streams hinzu und wundern sich dann, warum der Stream den Müll drosselt.

In der Kryptographie alles muss sehr sorgfältig geprüft werdenEin Angreifer versucht nicht, etwas zu versuchen. Wenn Sie eine PKCS7-Auffüllung angeben, kann der Stream die PKCS7-Auffüllung am Ende überprüfen und eine Ausnahme auslösen, wenn am Ende des Streams nicht die richtige Auffüllung gefunden wird.

Der Stream hat keine Möglichkeit zu wissen, dass das tatsächlich der Fall istcyphertext endet in der Mitte des Streams und nicht am Ende. Wie würden Sie es erwarten? In Crypto ist es die Regel, alle Anomalien zu kennzeichnen, und fehlerhaftes Padding am (scheinbaren) Ende des Streams ist etwas, worauf die Dokumentation hinweist, dass Sie eine Ausnahme verursachen.