2009-06-02 7 views
11

Ich versuche, einen Datei-Stream über einen Socket mit RijndaelManaged zu verschlüsseln und zu entschlüsseln, aber ich immer stoßen AusnahmeLänge der Daten zu entschlüsseln, ist ungültig

CryptographicException: Length of the data to decrypt is invalid. 
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 
    at System.Security.Cryptography.CryptoStream.FlushFinalBlock() 
    at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 

Die Ausnahme am Ende geworfen wird von die using-Anweisung in receiveFile, wenn die gesamte Datei übertragen wurde.

Ich habe versucht, im Internet zu suchen, aber fand nur Antworten auf Probleme, die auftreten, wenn Sie Codierung beim Verschlüsseln und Entschlüsseln einer einzelnen Zeichenfolge verwenden. Ich benutze einen FileStream, also gebe ich kein Encoding an, das verwendet werden sollte, also sollte das nicht das Problem sein. Dies sind meine Methoden:

private void transferFile(FileInfo file, long position, long readBytes) 
{ 
    // transfer on socket stream 
    Stream stream = new FileStream(file.FullName, FileMode.Open); 
    if (position > 0) 
    { 
     stream.Seek(position, SeekOrigin.Begin); 
    } 
    // if this should be encrypted, wrap the encryptor stream 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamEncryptor, CryptoStreamMode.Read); 
    } 
    using (stream) 
    { 
     int read; 
     byte[] array = new byte[8096]; 
     while ((read = stream.Read(array, 0, array.Length)) > 0) 
     { 
      streamSocket.Send(array, 0, read, SocketFlags.None); 
      position += read; 
     } 
    } 
} 

private void receiveFile(FileInfo transferFile) 
{ 
    byte[] array = new byte[8096]; 
    // receive file 
    Stream stream = new FileStream(transferFile.FullName, FileMode.Append); 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamDecryptor, CryptoStreamMode.Write); 
    } 
    using (stream) 
    { 
     long position = new FileInfo(transferFile.Path).Length; 
     while (position < transferFile.Length) 
     { 
      int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
      int read = position < array.Length 
         ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
         : streamSocket.Receive(array, SocketFlags.None); 
      stream.Write(array, 0, read); 
      position += read; 
     } 
    } 
} 

Dies ist die Methode, die ich verwende, um die Chiffren einzurichten. byte [] init ist ein generiertes Byte-Array.

private void setupStreamCipher(byte[] init) 
{ 
    RijndaelManaged cipher = new RijndaelManaged(); 
    cipher.KeySize = cipher.BlockSize = 256; // bit size 
    cipher.Mode = CipherMode.ECB; 
    cipher.Padding = PaddingMode.ISO10126; 
    byte[] keyBytes = new byte[32]; 
    byte[] ivBytes = new byte[32]; 

    Array.Copy(init, keyBytes, 32); 
    Array.Copy(init, 32, ivBytes, 0, 32); 

    streamEncryptor = cipher.CreateEncryptor(keyBytes, ivBytes); 
    streamDecryptor = cipher.CreateDecryptor(keyBytes, ivBytes); 
} 

Wer hat eine Idee, was ich falsch machen könnte?

Antwort

6

Es sieht für mich aus, als würden Sie den letzten Block nicht richtig senden. Sie müssen mindestens FlushFinalBlock() senden CryptoStream, um sicherzustellen, dass der letzte Block (den der empfangende Stream sucht) gesendet wird.

Übrigens, CipherMode.ECB is more than likely an epic fail in Bezug auf die Sicherheit für das, was Sie tun. Verwenden Sie mindestens CipherMode.CBC (Chiffre-Block-Verkettung), die tatsächlich die IV verwendet und macht jeden Block abhängig von der vorherigen.

EDIT: Whoops, der Verschlüsselungsstrom ist im Lesemodus. In diesem Fall müssen Sie sicherstellen, dass Sie EOF gelesen haben, damit CryptoStream mit dem letzten Block umgehen kann, anstatt nach readBytes anzuhalten. Es ist wahrscheinlich einfacher zu kontrollieren, ob Sie den Verschlüsselungsstrom im Schreibmodus ausführen.

Noch ein Hinweis: Sie können nicht davon ausgehen, dass Bytes in Gleichbytes aus. Blockchiffren haben eine feste Blockgröße, die sie verarbeiten, und wenn Sie nicht einen Chiffriermodus verwenden, der die Blockchiffre in eine Stromchiffre konvertiert, gibt es eine Auffüllung, die den Chiffriertext länger als den Klartext macht.

+1

Die FlushFinalBlock() -Methode in der „Schließabschnitt“ der Anweisung using

using(stream) { // } // calls Close() -> FlushFinalBlock()
ich die CipherMode ändert genannt wird, trat ich es nur als ein Beispiel, so dass Sie wissen, dass ich in jeder meine Chiffre nicht initialisieren "komischer Weg. Die readBytes in SendFile() wird noch nicht verwendet, ich habe vergessen, es zu entfernen. Ich lese bis zum Ende der Datei, also sollte das hier nicht das Thema sein. Ich dachte,
cipher.Padding = PaddingMode.ISO10126;
kümmerte sich um die Polsterung? Was kann ich ändern, damit es funktioniert? – Patrick

+0

Wenn der Verschlüsselungsstrom im Lesemodus ist, geht der letzte Block verloren, wenn Sie ihn entsorgen; Sie muss das Ende der Datei aus dem zugrunde liegenden Quelldatenstrom lesen, um den letzten Block zu erzeugen. –

+0

Als Antwort auf Jeffrey: Wenn ich versuche, stream.FlushFinalBlock() aufzurufen, heißt es NonSupportedException: FlushFinalBlock kann nicht zweimal im selben Stream aufgerufen werden. Bedeutet das nicht, dass ein Dateiende gelesen (und gesendet) wurde? – Patrick

0
cipher.Mode = CipherMode.ECB; 

Argh! Es ist fast immer eine schlechte Idee, einen eigenen Sicherheitscode zu schreiben.

+1

????? nicht wahr? Er benutzt Rijndael? Das ist nicht "rollen Sie Ihre eigenen". Obwohl es einen guten Punkt zu machen gibt, müssen Entwickler vorsichtig sein, wie sie Verschlüsselung verwenden. – Cheeso

+0

ECB schlägt fehl, da jeder Block unabhängig verschlüsselt ist. –

+0

Es spielt keine Rolle, welchen CipherMode ich benutze, ich bekomme immer noch die "Length of data ..." Ausnahme ... – Patrick

1

Nach dem Kommentar von Jeffrey Hantin, änderte ich ein paar Zeilen in ReceiveFile zu

using (stream) { 
    FileInfo finfo = new FileInfo(transferFile.Path); 
    long position = finfo.Length; 
    while (position < transferFile.Length) { 
     int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
     int read = position < array.Length 
        ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
        : streamSocket.Receive(array, SocketFlags.None); 
     stream.Write(array, 0, read); 
     position += read; 
    } 
} 

->

using (stream) { 
    int read = array.Length; 
    while ((read = streamSocket.Receive(array, read, SocketFlags.None)) > 0) { 
     stream.Write(array, 0, read); 
     if ((read = streamSocket.Available) == 0) { 
      break; 
     } 
    } 
} 

Und voila, arbeitet sie (wegen der immer so freundlich Polsterung, die ich didn kümmere dich nicht früher darum). Ich bin mir nicht sicher, was passiert, wenn Verfügbar 0 zurückgibt, obwohl nicht alle Daten übertragen wurden, aber in diesem Fall werde ich das später tun. Danke für deine Hilfe Jeffrey!

Grüße.

0

-Mine i entfernt nur die Polsterung und es funktioniert

kommentiert diese aus - cipher.Padding = PaddingMode.ISO10126;