Wie kommt es, dass das Versetzen des GCM-Authentifizierungstags am Ende eines Verschlüsselungsstroms eine interne Pufferung während der Entschlüsselung erfordert?

In Java puffert der "Standard" -AES / GCM-Provider SunJCE - während des Entschlüsselungsprozesses - intern 1)ls Eingabe verwendete verschlüsselte Byt oder 2)decrypted Bytes produziert als Ergebnis. Der Anwendungscode, der die Entschlüsselung durchführt, wird feststellen, dassCipher.update(byte[]) gibt ein leeres Byte-Array zurück undCipher.update(ByteBuffer, ByteBuffer) schreibe Länge 0 zurück. Wenn der Vorgang abgeschlossen ist,Cipher.doFinal() wird zurückkehrenall decodierte Bytes.

Die erste Frage lautet: Welche Bytes werden gepuffert, Nummer 1 oder Nummer 2?

Ich nehme an, dass die Pufferung nur während der Entschlüsselung und nicht während der Verschlüsselung auftritt, da erstens die Probleme, die durch diese Pufferung (kurz beschrieben) entstehen, bei meinem Java-Client nicht bei der Verschlüsselung von Dateien auftreten, die von der Festplatte gelesen werden, sondern immer auf der Serverseite , diese Dateien empfangen und entschlüsseln. Zweitens heißt es soHie. Nach meiner eigenen Erfahrung zu urteilen, kann ich nicht sicher sein, weil mein Kunde ein @ verwendCipherOutputStream. Der Client verwendet keine expliziten Methoden für die Cipher-Instanz. Daher kann ich nicht ableiten, ob interne Pufferung verwendet wird oder nicht, da ich nicht sehen kann, was die update- und final-Methode zurückgibt.

Meine wirklichen Probleme entstehen, wenn die verschlüsselten Dateien, die ich vom Client zum Server übertrage, groß werden. Im Großen und Ganzen meine ich über 100 MB.

Was dann passiert ist, dass Cipher.update () ein @ wirOutOfMemoryError. Offensichtlich aufgrund des wachsenden und wachsenden internen Puffers.

Auch trotz interner Pufferung und ohne von Cipher.update () empfangene Ergebnisbytes, Cipher.getOutputSize (int) meldet kontinuierlich eine ständig wachsende Zielpufferlänge. Daher ist mein Anwendungscode gezwungen, ein ständig wachsendes @ zuzuweiseByteBuffer das ist Feed in Cipher.update (ByteBuffer, ByteBuffer). Wenn ich versuche, einen Byte-Puffer mit geringerer Kapazität zu betrügen und weiterzuleiten, wirft die Aktualisierungsmethode einShortBufferException # 1. Zu wissen, dass ich riesige Bytepuffer für keinen Zweck erstelle, ist ziemlich demoralisierend.

Gegeben, dass die interne Pufferung die Wurzel allen Übels ist, dann besteht die naheliegende Lösung für mich darin, die Dateien in Blöcke von jeweils 1 MB aufzuteilen - ich habe nie Probleme, kleine Dateien zu senden, nur große. Es fällt mir schwer zu verstehen, warum interne Pufferung überhaupt stattfindet.

Das zuvor verlinkte SO antworte sagt, dass das GCM-Authentifizierungs-Tag "am Ende des Chiffretexts hinzugefügt" wird, aber dass es "nicht am Ende stehen muss" und diese Vorgehensweise "die Online-Natur der GCM-Entschlüsselung durcheinander bringt".

Warum bringt es den Server nur durcheinander, wenn das Tag am Ende steht?

Hier ist, wie meine Argumentation geht. Um ein Authentifizierungs-Tag oder MAC zu berechnen, verwendet der Client eine Art Hash-Funktion. Offenbar,MessageDigest.update() verwendet keinen ständig wachsenden internen Puffer.

Dann kann der Server auf der empfangenden Seite nicht genau dasselbe tun? Für den Anfang kann er die Bytes, wenn auch nicht authentifizierten, entschlüsseln, diese in seine Aktualisierungsfunktion des Hash-Algorithmus einspeisen und, wenn das Tag eintrifft, den Digest beenden und den vom Client gesendeten MAC überprüfen.

Ich bin kein Krypto-Typ, also bitte sprich mit mir, als ob ich sowohl dumm als auch verrückt wäre, aber liebend genug, um mich um einige zu kümmern =) Ich danke dir von ganzem Herzen für die Zeit, die du gebraucht hast, um diese Frage durchzulesen und vielleicht sogar etwas Licht zu werfen!

UPDATE # 1

Ich verwende kein AD (Associated Data).

UPDATE # 2

Wrote-Software, die die AES / GCM-Verschlüsselung mit Java demonstriert, sowie dasSecure Remote Protocol (SRP) und binäre Dateiübertragungen in Java EE. Der Front-End-Client ist in JavaFX geschrieben und kann verwendet werden, um die Verschlüsselungskonfiguration dynamisch zu ändern oder Dateien mithilfe von Chunks zu senden. Am Ende einer Dateiübertragung werden einige Statistiken zur Zeit für die Übertragung der Datei und zur Zeit für die Entschlüsselung des Servers angezeigt. Das Repository enthält auch ein Dokument mit einigen meiner eigenen GCM- und Java-bezogenen Recherchen.

Genießen:https: //github.com/MartinanderssonDotcom/secure-login-file-transfer

# 1

Es ist interessant festzustellen, dass mein Server, der die Entschlüsselung durchführt, die Verschlüsselung nicht selbst handhabt, sondern ein @ verwendeCipherInputStream, dann wird OutOfMemoryError nicht geworfen. Stattdessen kann der Client alle Bytes über die Leitung übertragen, aber irgendwo während der Entschlüsselung bleibt der Anforderungsthread auf unbestimmte Zeit hängen, und ich kann feststellen, dass ein Java-Thread (möglicherweise derselbe Thread) einen CPU-Kern vollständig auslastet, während die Datei aktiv bleibt Datenträger unzugänglich und mit einer gemeldeten Dateigröße von 0. Nach einer enorm langen ZeCloseable source ist geschlossen und meine catch-Klausel kann eine IOException abfangen, die durch Folgendes verursacht wird: "javax.crypto.AEADBadTagException: Eingabe zu kurz - Tag erforderlich".

Was diese Situation merkwürdig macht, ist, dass das Übertragen kleinerer Dateien mit genau demselben Code fehlerfrei funktioniert - daher kann das Tag offensichtlich ordnungsgemäß überprüft werden. Das Problem muss dieselbe Grundursache haben wie bei der expliziten Verwendung der Chiffre, d. H. Ein ständig wachsender interner Puffer. Ich kann auf dem Server nicht nachverfolgen, wie viele Bytes erfolgreich gelesen / entschlüsselt wurden, da die Compiler-Neuordnung oder andere JIT-Optimierungen alle meine Protokollanweisungen in Luft aufgehen lassen, sobald das Lesen des Verschlüsselungseingabestroms beginnt. Sie werden [anscheinend] überhaupt nicht ausgeführt.

Beachten Sie, dassdieses GitHub-Projekt und das zugehörigeBlogeintra sagt, dass CipherInputStream defekt ist. Bei Verwendung von Java 8u25 und des SunJCE-Providers schlagen die von diesem Projekt bereitgestellten Tests jedoch nicht fehl. Und wie schon gesagt, funktioniert bei mir alles, wenn ich nur kleine Dateien verwende.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage