| 

.NET C# Java Javascript Exception

4
Beim arbeiten mit Verschlüsselung bin ich auf ein Problem gestoßen.
Ich verschlüssle einen FileStream mittels eines CryptoStream (AES-256).
Danach möchte ich den CryptoStream wieder in die Datei schreiben.
Dazu übertrage ich die bytes aus dem CryptoStream in ein Byte Array.
Allerdings funktioniert dies für Große Dateien nicht (OutOfMemoryException).
private static void ReadAllBytesBigFile(Stream input, long rounds, string filePath)
{
int readBytes = 0;

//defines a byte array with a length of 1024 to temporary store bytes
byte[] buffer = new byte[1024];
//array list to store the bytes
ArrayList bufferRead = new ArrayList();
FileStream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
int t =0;

for (int i = 0; i <= rounds; ++i)
{
do
{
readBytes = input.Read(buffer, 0, buffer.Length);

byte[] tmp = new byte[readBytes];
//Copies completely the bytes of the buffer into the tmp byte array
Array.Copy(buffer, tmp, readBytes);
//Adds the tmp into the bufferRead arraylist
bufferRead.AddRange(tmp);
++t;
} while ((readBytes != 0)&&(t<65536));
Byte[] tempByte =(byte[])bufferRead.ToArray(typeof(byte));
stream.Write(tempByte, 0, tempByte.Length);
bufferRead.Clear();
t=0;
}
stream.Flush();
stream.Close();
input.Flush();
input.Close();
}

Ich habe versucht, wie oben, das Problem zu umgehen, indem ich den Stream in eine temporäre Datei schreibe ,und mit dem gleichen Code danach wieder in die Datei, die das ursprüngliche Ziel war.
Allerdings gibt obiger Code den Fehler

  • ContextSwitchDeadlock
    Die CLR konnte 60 Sekunden lang keinen Übergang vom COM-Kontext 0x4d5b70 zum COM-Kontext 0x4d5ce0 durchführen. Der Thread, der Besitzer des Zielkontexts/-apartments ist, wartet entweder, ohne Meldungen zu verschieben, oder verarbeitet eine äußerst lang dauernde Operation, ohne Windows-Meldungen zu verschieben. Eine solche Situation beeinträchtigt in der Regel die Leistung und kann sogar dazu führen, dass die Anwendung nicht mehr reagiert oder die Speicherauslastung immer weiter zunimmt. Zur Vermeidung dieses Problems sollten alle STA-Threads (Singlethread-Apartment) primitive Typen verwenden, die beim Warten Meldungen verschieben (z.B. CoWaitForMultipleHandles), und bei lange dauernden Operationen generell Meldungen verschieben.

    aus.

    Gibt es eine andere Möglichkeit den CryptoStream in die Datei aus der die Daten stammen zu schreiben, also ohne den Umweg über ein Byte Array oder eine neue Datei?
    Oder gibt es eine Möglichkeit den ContextSwitchDeadlock zu verhindern?
    Vielen Dank für eure Hilfe.

    ------------------------------------------------------------------------------------------

    Also ich präzisiere meine Frage nocheinmal, vielleicht kann mir dann eher jemand helfen.
    Ich schreibe ein Pogramm, mit dem man beliebig große Dateien verschlüsseln können soll.
    public static void EncryptFile(string filePath)
    {
    FileInfo info = new FileInfo(filePath);
    long fileSize = info.Length;

    AesManaged aes = new AesManaged();
    aes.KeySize = 256;
    aes.Key = Key;
    aes.IV = IV;

    //Reads a file into a filestream
    FileStream fsRead = new FileStream(filePath, FileMode.Open, FileAccess.Read);

    //Creates a CryptoStream and encrypts the filestream
    CryptoStream crypto = new CryptoStream(fsRead, aes.CreateEncryptor(), CryptoStreamMode.Read);
    //Transfers the CryptoStream into a byte array
    Byte[] encrypted = ReadAllBytes(crypto);

    //closes all access of the filestream
    fsRead.Flush();
    fsRead.Close();

    //Creates a new filestream to overwrite the file
    FileStream fsEnc = new FileStream(filePath, FileMode.Open, FileAccess.Write);

    //Writes the byte array with the encrypted data into the file
    fsEnc.Write(encrypted, 0, encrypted.Length);
    //closes all access of the filestream
    fsEnc.Flush();
    fsEnc.Close();
    }

    Dies ist die eigentliche Verschlüsselung. Da ich die verschlüsselten Daten wieder in die Datei schreiben möchte, aus der sie stammen, speichere ich die Daten in einem Byte Array zwischen, schließe alle Dateizugriffe und schreibe sie dann mit einem neuen FileStream wieder in die Datei.
    Das Problem ist die Zwischenspeicherung im Array, da dieser nach 64 MB Inhalt "gefüllt" ist.
    public static void EncryptFile(string filePath)
    {
    FileInfo info = new FileInfo(filePath);
    long fileSize = info.Length;

    AesManaged aes = new AesManaged();
    aes.KeySize = 256;
    aes.Key = Key;
    aes.IV = IV;

    //Reads a file into a filestream
    FileStream fsRead = new FileStream(filePath, FileMode.Open, FileAccess.Read);

    //Creates a CryptoStream and encrypts the filestream
    CryptoStream crypto = new CryptoStream(fsRead, aes.CreateEncryptor(), CryptoStreamMode.Read);
    //Transfers the CryptoStream into a byte array
    Byte[] encrypted = ReadAllBytes(crypto);

    //closes all access of the filestream
    fsRead.Flush();
    fsRead.Close();

    //Creates a new filestream to overwrite the file
    FileStream fsEnc = new FileStream(filePath, FileMode.Open, FileAccess.Write);

    //Writes the byte array with the encrypted data into the file
    fsEnc.Write(encrypted, 0, encrypted.Length);
    //closes all access of the filestream
    fsEnc.Flush();
    fsEnc.Close();
    }

    Gibt es einen Weg diesen Schritt zu umgehen, oder so anzupassen, das der Arbeitsspeicher nicht über gebühr beansprucht wird?
    Die Klasse MemoryMappedFile scheint mir eine Möglichkeit zu sein, jedoch verstehe ich nicht ganz wie ich sie benutzen muss.
    Vielleicht kann mir dies jemand erklären oder vielleicht weiß jemand auch noch einen anderen Lösungsweg.
  • News:
    23.02.2012
    Simon Rühle 147 1 7
    1
    Ein paar fragen...
    1) Was hat das mit den rounds auf sich? und warum ++i
    2) Warum input.Flush? du schreibst doch nichts in den Input.
    3) Wie rufst du dein Read auf? Ist das in einem eigenen Thread oder direkt im GUI Thread?

    Schau dir mal die Klasse MemoryMappedFile an, die ist genau dafür da extrem große Files zu lesen.
    Link: http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx

    Grüße
    Nicolai Schönberg 24.02.2012
    Die rounds sollen mitzählen, wieviel noch in die Byte Array List passen, also nach jedem hinzufügen kommt eine round hinzu. Außerdem bekommt die Methode mitgeteilt, wieoft sie eine ArrayList füllen muss, das ist das ++i.
    Den input.Flush() werde ich tatsächlich rausnehmen.
    Der Read ist in einer eigenen Klasse aber im gleichen Thread.

    Das MemoryMappedFile werde ich mir einmal näher anschauen.
    Simon Rühle 24.02.2012
    Wenn ich das richtig verstehe, übertrage ich den CryptoStream in ein MemoryMappedFile, beende den Lesezugriff des FileStream auf die zu verschlüsselnde Datei, und erstelle einen schreibenden FileStream und übertrage das MemoryMappedFile in diesen FileStream?
    Simon Rühle 24.02.2012
    1 Antwort
    1
    Also ich habe doch noch eine Möglichkeit gefunden.
    public static void EncryptFile2(string filePath)
    {
    AesManaged aes = new AesManaged();
    aes.KeySize = 256;
    aes.Key = Key;
    aes.IV = IV;

    //Reads a file into a filestream
    FileStream fsRead = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);

    //Creates a CryptoStream and encrypts the filestream
    CryptoStream crypto = new CryptoStream(fsRead, aes.CreateEncryptor(), CryptoStreamMode.Read);

    int pos=0;
    int readBytes = 0;

    //defines a byte array with a length of 1024 to temporary store bytes
    byte[] buffer = new byte[1024];
    //array list to store the bytes
    ArrayList bufferRead = new ArrayList();

    do
    {
    readBytes = crypto.Read(buffer, 0, buffer.Length);

    byte[] tmp = new byte[readBytes];
    //Copies completely the bytes of the buffer into the tmp byte array
    Array.Copy(buffer, tmp, readBytes);

    fsRead.Seek(pos, SeekOrigin.Begin);
    fsRead.Write(tmp, 0, readBytes);

    pos+=readBytes;

    } while (readBytes != 0);

    fsRead.Flush();
    fsRead.Close();
    }

    So funktioniert es, indem man nicht einen Array als Zwischenspeicher benutzt, sondern direkt in den FileStream schreibt. Allerdings funktioniert das Entschlüsseln so nicht korrekt, ich denke aber nicht, da dies mit diesem Thema zu tun hat.
    26.02.2012
    Simon Rühle 147 1 7

    Stelle deine .net-Frage jetzt!
    TOP TECHNOLOGIES CONSULTING GmbH