Langsam AES-GCM-Verschlüsselung und-Entschlüsselung mit Java 8u20
Ich versuche zu ver-und entschlüsseln von Daten unter Verwendung von AES/GCM/NoPadding. Ich installierte JCE Unlimited Strength Policy Files und ran an die (simple minded) benchmark unten. Ich habe das gleiche getan mit OpenSSL und war in der Lage, mehr zu erreichen, als 1 GB/s - Verschlüsselung und-Entschlüsselung auf meinem PC.
Mit der benchmark unten ich bin nur in der Lage, 3 MB/s - Verschlüsselung und-Entschlüsselung mit Hilfe von Java 8 auf dem gleichen PC. Keine Ahnung, was ich falsch mache?
public static void main(String[] args) throws Exception {
final byte[] data = new byte[64 * 1024];
final byte[] encrypted = new byte[64 * 1024];
final byte[] key = new byte[32];
final byte[] iv = new byte[12];
final Random random = new Random(1);
random.nextBytes(data);
random.nextBytes(key);
random.nextBytes(iv);
System.out.println("Benchmarking AES-256 GCM encryption for 10 seconds");
long javaEncryptInputBytes = 0;
long javaEncryptStartTime = System.currentTimeMillis();
final Cipher javaAES256 = Cipher.getInstance("AES/GCM/NoPadding");
byte[] tag = new byte[16];
long encryptInitTime = 0L;
long encryptUpdate1Time = 0L;
long encryptDoFinalTime = 0L;
while (System.currentTimeMillis() - javaEncryptStartTime < 10000) {
random.nextBytes(iv);
long n1 = System.nanoTime();
javaAES256.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(16 * Byte.SIZE, iv));
long n2 = System.nanoTime();
javaAES256.update(data, 0, data.length, encrypted, 0);
long n3 = System.nanoTime();
javaAES256.doFinal(tag, 0);
long n4 = System.nanoTime();
javaEncryptInputBytes += data.length;
encryptInitTime = n2 - n1;
encryptUpdate1Time = n3 - n2;
encryptDoFinalTime = n4 - n3;
}
long javaEncryptEndTime = System.currentTimeMillis();
System.out.println("Time init (ns): " + encryptInitTime);
System.out.println("Time update (ns): " + encryptUpdate1Time);
System.out.println("Time do final (ns): " + encryptDoFinalTime);
System.out.println("Java calculated at " + (javaEncryptInputBytes / 1024 / 1024 / ((javaEncryptEndTime - javaEncryptStartTime) / 1000)) + " MB/s");
System.out.println("Benchmarking AES-256 GCM decryption for 10 seconds");
long javaDecryptInputBytes = 0;
long javaDecryptStartTime = System.currentTimeMillis();
final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv);
final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
long decryptInitTime = 0L;
long decryptUpdate1Time = 0L;
long decryptUpdate2Time = 0L;
long decryptDoFinalTime = 0L;
while (System.currentTimeMillis() - javaDecryptStartTime < 10000) {
long n1 = System.nanoTime();
javaAES256.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
long n2 = System.nanoTime();
int offset = javaAES256.update(encrypted, 0, encrypted.length, data, 0);
long n3 = System.nanoTime();
javaAES256.update(tag, 0, tag.length, data, offset);
long n4 = System.nanoTime();
javaAES256.doFinal(data, offset);
long n5 = System.nanoTime();
javaDecryptInputBytes += data.length;
decryptInitTime += n2 - n1;
decryptUpdate1Time += n3 - n2;
decryptUpdate2Time += n4 - n3;
decryptDoFinalTime += n5 - n4;
}
long javaDecryptEndTime = System.currentTimeMillis();
System.out.println("Time init (ns): " + decryptInitTime);
System.out.println("Time update 1 (ns): " + decryptUpdate1Time);
System.out.println("Time update 2 (ns): " + decryptUpdate2Time);
System.out.println("Time do final (ns): " + decryptDoFinalTime);
System.out.println("Total bytes processed: " + javaDecryptInputBytes);
System.out.println("Java calculated at " + (javaDecryptInputBytes / 1024 / 1024 / ((javaDecryptEndTime - javaDecryptStartTime) / 1000)) + " MB/s");
}
EDIT:
Ich lass es als nette übung um das zu verbessern einfach gemerkt benchmark.
Ich getestet habe, etwas mehr über die ServerVM, entfernt nanoTime Anrufe und eingeführt, warm-up, aber wie ich es erwartet keiner von diesen hatte jede Verbesserung auf die benchmark-Ergebnisse. Es ist flach-gefüttert in 3 Megabyte pro Sekunde.
- Erstens, der Vergleich ist falsch: kein warmup, iteration, übermäßige nanoTime Anrufe. Hotspot ist die systeminterne Funktionen für AES-NI sind nur mit einem optimierenden JIT-compiler, die Sie haben, um es zu erreichen, bevor die Beurteilung der Leistung. Zweitens versuchen, AES/CBC. Tun Sie tatsächlich zu Messen, aes-gcm mit OpenSSL, und es gab Sie 1 GB/s?
- Beachten Sie auch, dass zur Verwendung der AES-NI-Interna, es ist erforderlich für die Verwendung der Server-VM, eine moderne Intel-CPU mit Unterstützung, und haben ein warm-up Sequenz. Beachten Sie, dass OpenSSL ist eine der am schnellsten libs gibt, byte-code kann relativ schnell für die business-Logik, aber für Kryptografie sehen Sie Unterschiede mit gut umgesetzten C/C++ - Bibliotheken.
- Ja, ich weiß, das ist nicht der robusteste benchmark, aber 3 MB/s vs. 1 Gbit/s ist immer noch sehr wichtig und ich fühle mich auf diese einfältige benchmark ist gut genug, um den Punkt über. Ich habe versucht, AES/CBC, und ich bin in der Lage, mehr als 400 MB/s für die Verschlüsselung und mehr als 1 GB/s für die Entschlüsselung, die mit Java ' s cipher.
- Ich Schreibe meine eigenen JavaFX - /Java-EE-Anwendung testen, mit einem tollen GUI, gehen Sie durch den gesamten Prozess der Authentifizierung eines Benutzers mithilfe von SRP und dann das senden von verschlüsselten Dateien über WebSocket mit AES/GCM. Ich zurück, mit einem link, wenn die app fertig ist. Aber für jetzt, alles, was ich sagen wollte ist, dass im Vergleich mit unverschlüsselten Datei übertragen, unter Verwendung von AES/GCM ist etwa 10-mal langsamer für mich (96-bit-Authentifizierungs-Tags, 128-bit-Schlüssel & IV).
- Sie haben nicht die Unlimited Strength Jurisdiction Policy-Dateien installiert.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Micro-benchmarking beiseite, die Leistung des GCM-Implementierung im JDK 8 (zumindest bis zu 1.8.0_25) ist verkrüppelt.
Kann ich konsequent zu reproduzieren, die 3MB/s (auf einem Haswell i7 laptop) mit einem reiferen micro-benchmark.
Vom code Tauchen, dies scheint durch eine naive Multiplikator Umsetzung und keine hardware-Beschleunigung für die GCM-Berechnungen.
Durch Vergleich der AES (im ECB-oder CBC-Modus) im JDK 8 verwendet einen AES-NI beschleunigt intrinsische und ist (für Java zumindest) sehr schnell (in der Reihenfolge von 1 GB/s auf der gleichen hardware), aber die gesamte AES/GCM-Leistung ist völlig dominiert von der zerbrochenen GCM Leistung.
Gibt es Pläne, um die hardware-Beschleunigung, und es wurden Eingaben von Dritten zur Verbesserung der Leistung mit, aber diese noch nicht zum release noch.
Etwas anderes zu beachten ist, dass die JDK-GCM-Implementierung auch Puffer die gesamte Klartext auf Entschlüsselung, bis die Authentifizierung tag am Ende der Geheimtext ist verifiziert, die Krüppel, die es für den Einsatz mit großen Nachrichten.
Hüpfburg hat (zum Zeitpunkt des Schreibens) schneller GCM-Implementierungen (und OCB, wenn Sie schreiben, open-source-software nicht frei von software-patent-Gesetze).
Aktualisiert: Juli 2015 - 1.8.0_45 und JDK 9
JDK 8+ erhalten eine verbesserte (und Konstante Zeit) Java-Implementierung (contributed by Florian Weimer von RedHat) - dies ist gelandet in JDK 9 EA baut, aber anscheinend noch nicht in 1.8.0_45.
JDK9 (da EA b72 zumindest) hat auch GCM-Interna - AES/GCM Geschwindigkeit auf b72 ist 18MB/s, ohne Interna aktiviert und 25MB/s mit Interna aktiviert, beide sind enttäuschend - zum Vergleich, die Schnellste (nicht Konstante Zeit) BC Umsetzung ist ~60MB/s und der langsamste (Konstante Zeit, nicht vollständig optimierten) ist ~26 MB/s.
Aktualisiert Jan 2016 - 1.8.0_72:
Einige performance fixes landete im JDK 1.8.0_60 und Leistung auf dem gleichen benchmark nun ist 18MB/s - 6x Verbesserung gegenüber dem original, aber immer noch viel langsamer als die BC-Implementierungen.
Diese wurde nun teilweise in Angriff genommen in Java 8u60 mit JDK-8069072. Ohne dieses Update bekomme ich 2,5 M/s. Mit diesem Update bekomme ich 25M/s. Deaktivieren GCM komplett gibt mir 60M/s.
Deaktivieren GCM komplett erstellen Sie eine Datei namens
java.security
mit der folgenden Zeile:Dann starten Sie Ihre Java-Prozess mit:
Wenn das nicht funktioniert, Sie benötigen, um übergeordnete Sicherheits-Eigenschaften durch die Bearbeitung
/usr/java/default/jre/lib/security/java.security
(tatsächliche Pfad kann unterschiedlich sein, je nach OS) und hinzufügen:/dev/urandom
: beginnen Sie mitjava -Djava.security.egd=file:/dev/urandom …
(aus den Kommentaren in<java-root>/jre/lib/security/java.security
).OpenSSL-Implementierung wird optimiert, indem die assembly-routine mit pclmulqdq-Instruktion(x86-Plattform). Es sehr schnell durch die parallel-Algorithmus.
Die java-Implementierung zu langsam ist. aber es wurde auch optimiert Hotspot mit assembly-routine(nicht parallel). Sie haben zum Aufwärmen die jvm zu verwenden, Hotspot immanent. Der default-Wert von -XX:CompileThreshold ist 10000.
//pseudocode
warmUp_GCM_cipher_loop10000_times();
do_benchmark();