C# Daten verschlüsseln Rijndael / AES

Ghost_Rider_R

Lieutenant
Registriert
Nov. 2009
Beiträge
752
Hallo zusammen,

ich verwende die Klasse RijndaelManaged um Daten (unter anderem auch Zeichenketten) zu verschlüsseln und hätte da noch ein paar allgemeine Fragen dazu:

1. Mir ist aufgefallen, dass wenn ich unterschiedliche KeySize parameter (128, 192, 256) verwende, dass das Ergebnis unverändert bleibt, ist das so richtig? Ich hätte erwartet, dass jedes Mal ein anderes Ergebnis dabei raus kommt.

2. Ich verwende zur Key-Generation den SHA256 Algorithmus. Dieser gibt immer ein 32 Byte langes Array zurück. Wieso kann ich bei ausgewählter KeySize 128 einen 32 Byte langen Key hinterlegen, ohne dass es zu einem Fehler kommt? 32 Byte entsprechen ja 256 Bits und der Schlüssel darf ja eigentlich nur 128 Bit / 16 Byte lang sein.

3. Habe ich bei der Implementierung noch etwas wichtiges vergessen?

4. Welche Einstellungen muss ich noch setzten, damit der Rijndael Algoritumus den AES Standard erfüllt? AES verwendet ja diesen Algoritumus, aber nur mit bestimmten Optionen so wie ich das verstanden habe also z.B. nur Schlüssellänge 128 / 256 Bits. Rijndael könnte ja auch kürzere Schlüsselgrößen. Ansonsten ist das unten ja AES verschlüsselt, kann man das so sagen?

4. Ich habe das Problem, dass wenn ich Strings verschlüssele und wieder entschlüssele, dass dann am Ende der Zeichenkette dann immer \0\0\0\0\0\0 angehängt ist, was in der original Zeichenkette nicht vorhanden ist. Das müsste auf ein Padding zurückzuführen sein, das der Algo nur mit festen längen arbeitet. Wie bekomme ich das ,,sauber" raus? momentan schneide ich das einfach ab, aber das erscheint mir nicht als korrekter Weg.

Das sind die beiden verwendeten Methoden:

AES.jpg

Vielen Dank schon mal für eure Hilfe.

LG Ghost Rider
 

Anhänge

  • AES.txt
    2,1 KB · Aufrufe: 226
:o Ok...
Du hast noch ein paar Lektionen vor dir. :D
Da seh ich sofort ein paar eklatante Sicherheitsprobleme in deinem Code, die ich mal als erstes Ansprechen wollen würde, bevor ich auf deine Fragen eingehe:

  • Niemals, nienienienienienienienienienienienienienienienienienienienienienienienienienienienienieniemals eine einfache Hash-Funktion zum Erstellen eines Keys für eine Verschlüsselung verwenden!
Dafür gibt es extra spezielle Schlüsselableitungs-Hashfunktionen (Key derivation functions) wie z.B. Argon2, bcrypt, scrypt, or PBKDF2.​

  • Ein Initialization vector (IV) MUSS IMMER aus tatsächlicher Zufälligkeit bestehen und darf UNTER KEINEN UMSTÄNDEN mit Daten des restlichen Verschlüsselungsverfahrens oder den zu verschlüsselnden Daten in Verbindung stehen!
Dafür gibt es in jedem Betriebssystem entsprechende APIs, über die man sich einen Packen Entropie zur Verwendung abholen kann.​
Das lässt sich nicht nur für IVs verwenden, sondern auch für alle Salts!​

Ghost_Rider_R schrieb:
Hallo zusammen,

ich verwende die Klasse RijndaelManaged um Daten (unter anderem auch Zeichenketten) zu verschlüsseln und hätte da noch ein paar allgemeine Fragen dazu:

1. Mir ist aufgefallen, dass wenn ich unterschiedliche KeySize parameter (128, 192, 256) verwende, dass das Ergebnis unverändert bleibt, ist das so richtig? Ich hätte erwartet, dass jedes Mal ein anderes Ergebnis dabei raus kommt.

2. Ich verwende zur Key-Generation den SHA256 Algorithmus. Dieser gibt immer ein 32 Byte langes Array zurück. Wieso kann ich bei ausgewählter KeySize 128 einen 32 Byte langen Key hinterlegen, ohne dass es zu einem Fehler kommt? 32 Byte entsprechen ja 256 Bits und der Schlüssel darf ja eigentlich nur 128 Bit / 16 Byte lang sein.

3. Habe ich bei der Implementierung noch etwas wichtiges vergessen?

4. Welche Einstellungen muss ich noch setzten, damit der Rijndael Algoritumus den AES Standard erfüllt? AES verwendet ja diesen Algoritumus, aber nur mit bestimmten Optionen so wie ich das verstanden habe also z.B. nur Schlüssellänge 128 / 256 Bits. Rijndael könnte ja auch kürzere Schlüsselgrößen. Ansonsten ist das unten ja AES verschlüsselt, kann man das so sagen?

4. Ich habe das Problem, dass wenn ich Strings verschlüssele und wieder entschlüssele, dass dann am Ende der Zeichenkette dann immer \0\0\0\0\0\0 angehängt ist, was in der original Zeichenkette nicht vorhanden ist. Das müsste auf ein Padding zurückzuführen sein, das der Algo nur mit festen längen arbeitet. Wie bekomme ich das ,,sauber" raus? momentan schneide ich das einfach ab, aber das erscheint mir nicht als korrekter Weg.
1.: Das sollte so im Normalfall nicht passieren.
Da Rijndael aber ein Block Cipher ist, ist es theoretisch möglich ein Szenario zu konstruieren, bei dem mit allen drei Schlüssellängen dasselbe Ergebnis herauskommt.
Das setzt dann aber einen absichtlich fehlerhaften Einsatz des Algorithmus und auf einander abgestimmte Keys, IVs und einen passenden Verschlüsselungstext voraus.
Das gesagt, ich kenne die Implemtation "RijndaelManaged" nicht und habe es auch noch nie verwendet.
Ich kann mir aber vorstellen, dass du deine eingestellte KeySize mit dem Zuweisen eines 256bit langen Schlüssels immer wieder zunichte machst.

2. Ja... tu das nicht!;)
Die Algorithmusimplementation, mit der ich in dieser Detailtiefe zu tun hatte, hat nicht durch einen extrigen Parameter mit der entsprechenden Schlüssellänge verschlüsselt, sondern das an der tatsächlich übergebenen Schlüssellänge festgemacht.
Macht auch irgendwie Sinn, es kann nur ein Fehler sein, wenn verlangt wird einer spezifischen Schlüssellänge zu verschlüsseln, wenn dann aber weniger oder mehr zur Verfügung gestellt wird.

3. Einiges...Verschlüsselung ist schwer. :freak:
Dir fehlt eine Integritätsprüfung! Die Verschlüsselung selber verhindert nur das reinschauen in die Daten, sie schützt aber nicht vor Veränderung.
Ich weiß nicht, ob "RijndaelManaged" da schon etwas bietet oder mitliefert, aber wenn nicht, musst du das auch selber noch implementieren.
Das lässt ich z.B. mit HMAC machen, was einen weiteren Key erfordert.
Dabei musst du in etwa so vorgehen:
  1. Entropie für die IV anfordern und für weitere Verwendung vorhalten.
  2. Entropie für den RijndaelKey.Salt anfordern und für weitere Verwendung vorhalten.
  3. Entropie für den HMAC-Key.Salt anfordern und für weitere Verwendung vorhalten.
  4. Passwort und RijndaelKey.Salt durch deine Key Derivation Function jagen, die dir dann Bits in von dir beliebig vorgegebbarer Länge ausspuckt (egal, wie lang der Input ist).
  5. Das gleiche noch mal mit Passwort und HMAC.Salt für den HMACKey.
  6. Mit IV und RijndaelKey deinen Input verschlüsseln.
  7. IV, RijndaelKey.Salt und HMAC-Key.Salt vor deinen Ciphertext packen und das alles gemeinsam mit dem HMACKey durch deine HMAC-Funktion jagen.
  8. Das HMAC-Ergebnis zusammen mit dem IV-RijndaelKey.Salt-HMAC-Key.Salt-Ciphertext-BLOB in eine Datei speichern. Am besten gleich mit einem passenden Datei-Header für die Offsets und Längen der einzelenen Elemente.
Ich übernehme hierfür keine Garantie für Vollständigkeit. Kann gut sein, dass ich was vergessen habe oder in deinem Code übersehen habe (habe ihn nur überflogen)!
Des weiteren kenne ich, wie schon erwähnt, "RijndaelManaged" nicht.
Als nächsten Ansatz sollte es dir aber erstmal reichen.
Verschlüsselung heißt auch immer viel Recherchieren...man kann da so unglaublich viel falsch machen und die meisten Beispiele, die sich im Internet öffentlich finden sind spätestens beim näheren hinschauen brutaler Kackmist. (Microsoft verwendet z.B. in ihren Code-Beispielen für ihre CNG-API eine konstante IV und lauter Nullen und keine Key-Deriviation...)
Das gilt auch oft für OpenSource-Projekte, die selbst Verschlüsselung Implementieren und keine fertige API verwenden. (Wie z.B. LibSodium!)

4. Bei AES ist immer eine IV in der Länge von 128bit vorgeschrieben, egal wie lang der Schlüssel ist.
Kann gut sein, dass noch mehr zu beachten ist, habe ich aber grade nicht im Kopf.

5. Einige Implementationen nehmen dir das Abschneiden des Paddings ab. Geschieht das nicht automatisch, musst du es selber machen. 😝
Zu beachten ist hier aber, dass das unterschiedlich gehandhabt wird. Manche Implementationen padden einfach nur Nullen, andere zählen hoch, andere schreiben Zufallszahlen rein, etc.
Wenn bei dir immer Nullen dran hängen und du sicherstellen kannst dass deine Texte/Daten am Ende keine Null-Bytes haben, kannst du dasja recht einfach erkennen.
Wenn nicht, musst du u.U. die Text-, bzw. Datenlänge vor den Text packen und mit verschlüsseln oder dir was anderes, passendes ausdenken.


BONUS:
6. Nutzt für produktive Einsätze unbedingt etablierte Bibliotheken!
Für akademische Zwecke, Fortbildung oder einfach aus Interesse, ist es aber alles andere als verkehrt, damit herumzuspielen! :daumen:
 
  • Gefällt mir
Reaktionen: breedmaster und Dalek
Was ich nicht verstande habe ist. warum die Padding Bytes im Cipher hier ein Problem sind.
Das die KeySize Angabe bei .Net Rjandel keine Änderung verursacht, ist leicht zu erklären. Es ist lediglich eine Angabe wie lang der Key maximal sein darf. Weitere Libs braucht es meiner Meinung nach für eine .Net Nutzung von Rjandel ja nicht. Aber das ist nur meine Meinung :)
 
AW4 schrieb:
Dafür gibt es in jedem Betriebssystem entsprechende APIs, über die man sich einen Packen Entropie zur Verwendung abholen kann.
Das lässt sich nicht nur für IVs verwenden, sondern auch für alle Salts!
Ganz vergessen, aber sehr wichtig:
Das darf man unter keinen Umständen mit den üblichen Pseudo-Random-Generatoren, wie Random.Next() in C#, verwechseln!
Die sind nicht zufällig!

Hier eine erleuchtende Auseinandersetzung mit diesem Thema in C#:
https://stackify.com/csharp-random-numbers/
 
Das sollte hier noch ale generelle Information für zukünftige Leser hin:
Cryptographic Best Practices

Oder wenn sich jemand dafür interessiert bzw. einen Einspringspunkt in Sachen Crypto sucht.
Wollt ihr tiefer einsteigen und eure eigene Crypto schreiben (egal ob das direkte Implementieren eines spezifischen, kryptographischen Algorithmus oder die Implementierung der Verwendung von einem oder von mehreren kryptographischen Algorithmem), beachtet bitte IMMER:
Don't Publish! Don't Use!
 
Zuletzt bearbeitet: (Überarbeitet)
AW4 schrieb:
Niemals, nienienienienienienienienienienienienienienienienienienienienienienienienienienienienieniemals eine einfache Hash-Funktion zum Erstellen eines Keys für eine Verschlüsselung verwenden!
Selbstverständlich ist der Einsatz einer speziellen Schlüsselableitungsfunktion (KDF) empfehlenswert, besitzt diese doch einstellbare Work-Faktoren.

Aber: So ganz verstehe ich die absolute Ablehnung nicht; eine (kryptographische) Hash-Funktion erfüllt genau die Eigenschaften, die man an einen Schlüssel stellt. Als Probleme bleiben Bruteforce und Wörterbuchattacken. Entsprechend basieren viele KDFs basieren darauf, eine Hashfunktion in mehreren Iterationen (gegen Bruteforce) auf das Passwort + einen Salt (gegen Wörterbuchattacken) anzuwenden. Da sind wir doch nicht so weit von einer Hashfunktion weg. Eine HKDF (HMAC-based Extract-and-Expand Key Derivation Function, RFC5869; gibt es auch mit SHA256) baut im wesentlichen nach diesem Schema auf.

Daran wird das obige Verfahren also eher nicht scheitern. Ich würde formulieren: "Besser nicht eine einfache Hash-Funktion zum Erstellen eines Keys für eine Verschlüsselung verwenden."
 
Kanibal schrieb:
Selbstverständlich ist der Einsatz einer speziellen Schlüsselableitungsfunktion (KDF) empfehlenswert, besitzt diese doch einstellbare Work-Faktoren.
Da hast du etwas falsch gelesen.
Ich habe nirgendwo das Gegenteil behauptet.
Ich bin lediglich darauf eingegangen, dass der TE die normale SHA256-Hash-Funktion nutzt, um den Schlüssel für seine Crypto abzuleiten und eben genau nicht auf eine KDF zurückgreift.
 
AW4 schrieb:
Da hast du etwas falsch gelesen.
Ich denke, ich habe das ganz richtig gelesen. Ich wollte mit meinem Eingangssatz Deine These unterstützen.

Bleibt der Rest des Beitrags.
 
Ahhh, ok :D
Ich hab dich flasch verstanden.
 
  • Gefällt mir
Reaktionen: Kanibal
Zurück
Oben