Laden...

Verschlüsseln von DOCX Dateien anders als normale Dateien?

Erstellt von Killerhase vor 12 Jahren Letzter Beitrag vor 12 Jahren 5.070 Views
K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren
Verschlüsseln von DOCX Dateien anders als normale Dateien?

Hallo zusammen,

ich stehe aktuell vor einem Problem, dass ich leider nicht verstehe. Ich habe eine Funktion um Dateien zu verschlüsseln sowie zum entschlüsseln.

Diese funktionieren auch wunderbar für z.B. TIF, PDF, XML, DOC Dateien. Sobald ich jedoch eine DOCX Datei damit verschlüssel und danach wieder entschlüssel, erhalte ich beim öffnen der DOCX Datei folgende Fehlermeldung:

Fehlermeldung:
Die Datei 'test.docx' kann nicht geöffnet werden, da ihr Inhalt Probleme verursacht.

Unter Details steht dann noch:

Fehlermeldung:
Die Datei ist beschädigt und kann nicht geöffnet werden.

Nach dieser Meldung frägt Word ob man die entsprechende Datei wiederherstellen möchte. Das funktioniert auch und der Inhalt kann in Word normal dargestellt werden. Die Frage ist nur was genau den Fehler produziert.

Ich bereite mich gerade auf die Prüfung MCTS 70-536 vor und habe den entsprechenden Code auch aus dem entsprechenden Buch von MS entnommen. Wie gesagt für andere Dateitypen funktioniert das ganze auch wunderbar.
Jetzt ist die Frage, ob bei gewissen Dateien nur bestimmte Klassen zum verschlüsseln Nutzen darf. Bzw. wo der Unterschied beim Verschlüsseln von Word .DOCX und Word .DOC Dateien liegt.

Ich verwende die RijndaelManaged-Klasse zum ver- und entschlüsseln der Dateien.

Sourcecode wenn benötigt kann ich natürlich posten, ich habe jedoch die Befürchtung, dass ich eventuell die falsche Klasse hierfür verwende.

Vielen Dank schon mal für eure Ideen.

Gelöschter Account
vor 12 Jahren

Zeig doch mal etwas Code damit man das nachvollziehen kann.
Hast du mal das Encoding übeprüft? Also setzt du UTF-8?

K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren

Hier mal die Funktion zum verschlüsseln.


public static void TestEncrypt(string sInput, string sOutput)
{ 
byte[] saltValueBytes = Encoding.ASCII.GetBytes(sKey);
Rfc2898DeriveBytes passwordKey = new Rfc2898DeriveBytes(sKey, saltValueBytes);

RijndaelManaged alg = new RijndaelManaged();
alg.Key = passwordKey.GetBytes(alg.KeySize / 8);
alg.IV = passwordKey.GetBytes(alg.BlockSize / 8);

FileStream inFile = new FileStream(sInput, FileMode.Open, FileAccess.Read);
byte[] fileData = new byte[inFile.Length];
inFile.Read(fileData, 0, (int)inFile.Length);

ICryptoTransform encryptor = alg.CreateEncryptor();
FileStream outFile = new FileStream(sOutput, FileMode.OpenOrCreate, FileAccess.Write);
CryptoStream encryptStream = new CryptoStream(outFile, encryptor, CryptoStreamMode.Write);

encryptStream.Write(fileData, 0, fileData.Length);

encryptStream.Close();
inFile.Close();
outFile.Close();
}

Und die entsprechende Funktion zum entschlüsseln.


public static void TestDecrypt(string sInput, string sOutput)
{
byte[] saltValueBytes = Encoding.ASCII.GetBytes(sKey);
Rfc2898DeriveBytes passwordKey = new Rfc2898DeriveBytes(sKey, saltValueBytes);

RijndaelManaged alg = new RijndaelManaged();
alg.Key = passwordKey.GetBytes(alg.KeySize / 8);
alg.IV = passwordKey.GetBytes(alg.BlockSize / 8);

ICryptoTransform decryptor = alg.CreateDecryptor();
FileStream inFile = new FileStream(sInput, FileMode.Open, FileAccess.Read);

CryptoStream decryptStream = new CryptoStream(inFile, decryptor, CryptoStreamMode.Read);

byte[] fileData = new byte[inFile.Length];
decryptStream.Read(fileData, 0, fileData.Length);

FileStream outFile = new FileStream(sOutput, FileMode.OpenOrCreate, FileAccess.Write);
outFile.Write(fileData, 0, fileData.Length);

decryptStream.Close();
inFile.Close();
outFile.Close();
}

Encoding wird hier nicht festgelegt. Wie gesagt Code ist so direkt aus dem Buch übernommen aber das Encoding macht eventuell Sinn.

Edit: Ich habe den Code Testweise erweitert und verschiedene Encodings verwendet. Sobald ich z.B. UTF8 verwende, verändert sich die Dateigröße der verschlüsselten Datei enorm und die Datei kann mit Word nicht mehr geöffnet werden. Selbst das reparieren der Datei schlägt nun fehl. Sobald ich z.B. Encoding.Default verwende, kann die Datei wieder mit der Fehlermeldung aus dem ersten Beitrag geöffnet und repariert werden.

C
2.122 Beiträge seit 2010
vor 12 Jahren

Verschlüsselungen sollten nicht mit Strings gemacht werden, wenn der Inhalt kein String ist. Bei Bilddateien ist das wenns nicht tödlich ist, immerhin schlechter Stil. Und auch bei Textdateien würde ich darauf verzichten.
Nimm reine Bytes zum Verschlüsseln, egal was drin steht.
Das heißt, lade die Dateien als byte[] und schreib sie so auch wieder. Dann müsste das auch funktionieren.

K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren

Hi,

erstmal danke für die Antwort aber genau das sollte ja der oben genannte Code machen. Wie gesagt ist direkt von MS der Code.

Und hier:

FileStream inFile = new FileStream(sInput, FileMode.Open, FileAccess.Read);
byte[] fileData = new byte[inFile.Length];
inFile.Read(fileData, 0, (int)inFile.Length);

passiert ja genau das, dass die Daten in einen byte[] geladen werden um anschließend geschrieben zu werden. Um das Encoding zu testen, habe ich den Code dann entsprechend geändert und mit Strings gearbeitet. Gleiches Ergebnis. Darum wieder zurück zum byte[]

D
91 Beiträge seit 2005
vor 12 Jahren

Zum OpenXML-SDK gibt es ein Tool, mit dessen Hilfe du dir den Inhalt der OpenXML-Datei (docx etc.) anzeigen lassen kannst.
Dort gibt es auch ein diff-Tool, das maßgeschneidert auf diese Dateien ist.
Vielleicht kannst du deinem Fehler so auf die Sprünge kommen. Mich würde das Ergebnis interessieren, da ich auf der Arbeit auch mit OpenXML-Dateien arbeite.

VG, Florian

C
2.122 Beiträge seit 2010
vor 12 Jahren

aber genau das sollte ja der oben genannte Code machen.

Ahso, ich hab den nicht genau angesehen. Hab nur "Input" als Eingabedaten gesehen und dann die Diskussion übebr die Encodings...
Was ist sKey und woher kommt es?
Ich denke es liegt an den Berechnungsmethoden fürs Salt. Hast du schon mal feste Werte eingesetzt, also direkt ein byte[] angeben statt eines zu berechnen?
Spätestens dann müsste es doch funktionieren und dan nkannst du dein Problem einschachteln.
Und ich würde auch feste byte[] ver/entschlüsseln lassen, dann hast du nicht so einen Aufwand bei der Prüfung obs auch richtig war.

K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren

sKey habe ich als konstanten string definiert. Also einfach

const string sKey "12345678";

Das komische wie gesagt ist, dass das verschlüsseln vieler anderer Dateitypen ja funktioniert. Normale .DOC Dateien funktionieren aber eben z.B. DOCX nicht.

Werde mal versuchen nen festen byte[] zu übergeben.

Danke

6.862 Beiträge seit 2003
vor 12 Jahren

Hallo,

hast du die Dateien vor der Verschlüsselung und Danach mal binär verglichen ob die identisch sind?

Wie schauts aus mit zip Dateien? Docx sind nämlich auch nur Containerdateien im Zip Format.

Baka wa shinanakya naoranai.

Mein XING Profil.

K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren

Ich habe jetzt das ganze mal mit ZIP Dateien (erstellt von Windows) versucht.

Ergebnis: Verschlüssel ich die ZIP Datei die wiederum die DOCX Datei enthält und entschlüssel diese Datei, funktioniert die DOCX Datei wunderbar.

Mit dem selben Code die DOCX direkt verschlüsselt stosse ich wieder auf das selbe Problem. 🤔

Die Daten sind nach dem entschlüsseln ca. 4 Byte größer als die Original Datei. Was wiederum bei z.B. ZIP, DOC, TIF, PDF nicht zu stören scheint. Habe das ganze jetzt auch mal mit XLSX Files versucht. Gleiches Problem wie bei den DOCX Files. Alte 2003 Excel Files funktionieren. Neue nicht.

Was mich auch irritiert, das Office die Dateien wiederherstellen kann und der Inhalt danach richtig angezeigt wird.

C
2.122 Beiträge seit 2010
vor 12 Jahren

Die Daten sind nach dem entschlüsseln ca. 4 Byte größer als die Original Datei.

Was heißt ca? Was hängt da noch dran? Hängts am Ende?

Wenn an einem Bild irgendwelcher Unsinn dran hängt, stört das ein Anzeigeprogramm wahrscheinlich nicht. Das hört einfach auf zu lesen wenn es merkt dass alles fürs Bild wichtige bereits gelesen ist. Selbes gilt wahrscheinlich für zip und sonstiges. Bei anderen Dateien und Programmen klappts wiederum nicht.
Aber deswegen "funktioniert" die Verschlüsselung nicht, nur weil ein Programm fälschlicherweise angehängte Daten ignorieren kann.

Beziehe dich bei deinem Test auf Fakten, indem du Dateien wirklich miteinander vergleichst. Sonst suchst du da noch ewig. Nicht irgendwas reinstecken, wieder irgendwas rausholen und feststellen dass ein Programm eine Datei trotzdem noch liest, ohne zu wissen was mit der wirklich passiert ist.

6.862 Beiträge seit 2003
vor 12 Jahren

Hallo,

Ergebnis: Verschlüssel ich die ZIP Datei die wiederum die DOCX Datei enthält und entschlüssel diese Datei, funktioniert die DOCX Datei wunderbar.

So meinte ich das nicht. Docx Dateien sind zip Dateien. Wenn zip Dateien tun, sollte auch Docx tun. Tuts ja auch, die Daten sind ja nutzbar, nur wird Word vermutlich wegen der geänderten Dateigröße rummotzen, denn

Die Daten sind nach dem entschlüsseln ca. 4 Byte größer als die Original Datei Das ist definitiv ein Fehler. Die Dateien müssen nach dem entschlüsseln genauso groß sein wie davor. Bzw. nicht nur genau groß, sondern identisch bis aufs letzte Bit.

Baka wa shinanakya naoranai.

Mein XING Profil.

K
Killerhase Themenstarter:in
79 Beiträge seit 2006
vor 12 Jahren

Ich habe das Problem jetzt lösen können mit einem weiteren Beispiel von Codeprojekt. Hier mal der modifizierte Code der Funktion:



public static void TestDecrypt(string sInput, string sOutput)
{
             byte[] saltValueBytes = Encoding.ASCII.GetBytes(sKey);
             Rfc2898DeriveBytes passwordKey = new Rfc2898DeriveBytes(sKey, saltValueBytes);

             RijndaelManaged alg = new RijndaelManaged();
             alg.Key = passwordKey.GetBytes(alg.KeySize / 8);
             alg.IV = passwordKey.GetBytes(alg.BlockSize / 8);

             ICryptoTransform decryptor = alg.CreateDecryptor();
             FileStream fsIn = new FileStream(sInput, FileMode.Open, FileAccess.Read);
             FileStream fsOut = new FileStream(sOutput, FileMode.OpenOrCreate, FileAccess.Write);
             CryptoStream decryptStream = new CryptoStream(fsOut, decryptor, CryptoStreamMode.Write);

             byte[] fileData = new byte[fsIn.Length];
             fsIn.Read(fileData, 0, fileData.Length);
             decryptStream.Write(fileData, 0, fileData.Length);

             decryptStream.Close();
             fsIn.Close();
             fsOut.Close();
}

Der Fehler lag in der TestDecrypt Methode. Anstelle mit dem FielStream fsOut die Daten zu schreiben, wurde die Methode entsprechend geändert, so das die Daten mit dem decryptStream geschrieben anstatt gelesen zu werden.


CryptoStream decryptStream = new CryptoStream(fsOut, decryptor, CryptoStreamMode.Write);

byte[] fileData = new byte[fsIn.Length];
fsIn.Read(fileData, 0, fileData.Length);
decryptStream.Write(fileData, 0, fileData.Length);

Ich verstehe hierbei allerdings nicht, wieso es erstens mit anderen Dateien funktioniert hat und wieso der Code von MS es genau anders (wie eben als erstes geschrieben) macht. Der neue Code funktioniert jetzt auf jedenfall mit allen Daten. Die Dateien sind jetzt so wie es gehört exakt gleich groß wie die Original Dateien.

Vielleicht habt ihr eine Idee zu dem Fall. Man will ja auch verstehen wieso etwas so funktioniert 😃

C
2.122 Beiträge seit 2010
vor 12 Jahren

Ich hab mir das mal abgeschrieben und ausprobiert.
In decryptStream.Read(fileData, 0, fileData.Length); werden in die Ausgabe die eigentlichen Eingabebytes geschrieben.
Wenn man den Rückgabewert von Read anschaut, sind das genau die Bytes die man reingeschrieben hat.
Der Rest was noch hintendran hängt sind die Bytes, die aus dem Unterschied zwischen der Länge der verschlüsselten und der entschlüsselten Daten rauskommen. Das Zielarray ist länger als nötig, weil es mit der Länge der verschlüsselten Daten erzeugt wird.
Das heißt du müsstest am Ende deiner Dateien ein paar Nullbytes stehen haben.

Ist natürlich schon schwach wenn original Microsoftcode sowas komisches macht.
Aber es zeigt dass die Sache auch mit Read funktioniert. Macht in der Überlegung ja schon auch Sinn. Der CryptoStream wird mit den verschlüsselnden Daten erzeugt und kennt die daher schon. Dann sind die schon drin und werden durch Read entschlüsselt und ausgelesen, nämlich in das Array. Dieses hat nur eine falsche Lägnge und enthält am Ende Überbleibsel.

Wer es nachspielen will, bitte fehlende using und Dispose oder Close usw. großzügig übersehen.


      byte[] encrypted;

      {
        RijndaelManaged alg = new RijndaelManaged();
        alg.Key = new byte[alg.KeySize / 8];
        alg.IV = new byte[alg.BlockSize / 8];

        byte[] plain = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };

        ICryptoTransform encryptor = alg.CreateEncryptor();
        MemoryStream ms = new MemoryStream();
        CryptoStream encryptStream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
        encryptStream.Write(plain, 0, plain.Length);
        encryptStream.Close();
        encrypted = ms.ToArray();
      }

      {
        RijndaelManaged alg = new RijndaelManaged();
        alg.Key = new byte[alg.KeySize / 8];
        alg.IV = new byte[alg.BlockSize / 8];

        ICryptoTransform decryptor = alg.CreateDecryptor();
        CryptoStream decryptStream = new CryptoStream(new MemoryStream(encrypted), decryptor, CryptoStreamMode.Read);

        byte[] fileData = new byte[encrypted.Length];
        int i = decryptStream.Read(fileData, 0, fileData.Length);

        MemoryStream outFile = new MemoryStream();
        outFile.Write(fileData, 0, fileData.Length);
        decryptStream.Close();

        byte[] plainagain = outFile.ToArray();
      }