Lent AES GCM chiffrement et le déchiffrement de Java 8u20
Je suis en train de chiffrer et déchiffrer les données en utilisant AES/GCM/NoPadding. J'ai installé la JCE Force Illimitée de la Politique de Fichiers et a couru la (les simples d'esprit) de référence ci-dessous. J'ai fait la même à l'aide d'OpenSSL et a été en mesure d'atteindre plus de 1 GB/s de chiffrement et de déchiffrement sur mon PC.
Avec la référence ci-dessous, je suis seulement en mesure d'obtenir 3 MO/s de chiffrement et de déchiffrement à l'aide de Java 8 sur le même PC. Toute idée de ce que je fais de mal?
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:
Je laisse comme un exercice amusant pour améliorer ce simple d'esprit indice de référence.
J'en ai testé quelques uns de plus à l'aide de la ServerVM, retiré nanoTime appels et introduit de chauffe, mais comme je m'y attendais rien de cela n'a eu aucune amélioration sur les résultats de la référence. Il est plafonné à 3 mégaoctets par seconde.
- Tout d'abord, l'analyse comparative est faux: pas de warm-up, seule itération, excessive nanoTime appels. Zone intrinsèques de l'AES-NI sont utilisés uniquement avec une optimisation de compilateur JIT, vous devez y arriver avant l'évaluation de la performance. Ensuite, essayez de AES/CBC. Pensez-vous réellement mesurer aes-gcm avec OpenSSL, et il vous a donné 1 GB/s?
- Notez aussi que pour utiliser AES-NI intrinsèques, il est nécessaire d'utiliser le Serveur de VM, un moderne type de PROCESSEUR Intel, avec le soutien, et d'avoir un échauffement de la séquence. Notez que OpenSSL est l'un des plus rapides libs y, byte code peut être relativement rapide pour la logique métier, mais pour la cryptographie, vous verrez les différences avec bien mis en œuvre bibliothèques C/C++.
- Oui je sais que ce n'est pas le plus solide de référence, mais à 3 MO/s vs 1 GB/s est encore très important et je sens que cette simpleminded de référence est assez bon pour donner le point à travers. J'ai essayé AES/CBC et je suis en mesure d'obtenir plus de 400 MO/s pour le chiffrement et plus de 1 GO/s pour le décryptage à l'aide de Java de l'algorithme de chiffrement.
- Je suis en train d'écrire mon propre JavaFX/Java EE de l'application de test avec un impressionnant GUI qui vont à travers l'ensemble du processus d'authentification d'un utilisateur à l'aide de PRS et ensuite envoyer des fichiers cryptés sur WebSocket en utilisant AES/GCM. Je vais revenir avec un lien lorsque l'application est faite. Mais pour l'instant, tout ce que je voulais dire, c'est que, par rapport chiffré de transfert de fichier, en utilisant AES/GCM est environ 10 fois plus lent pour moi (96 bits d'authentification balise de 128 bits, la clé & IV).
- vous n'avez pas le Illimité de la Force de la Compétence de la Politique de Fichiers installé.
Vous devez vous connecter pour publier un commentaire.
Micro-analyse comparative de côté, la performance de la GCM mise en œuvre dans le JDK 8 (au moins jusqu'à 1.8.0_25) est paralysé.
Je peux reproduire systématiquement les 3 MO/s (sur un Haswell i7 portable) avec une plus grande maturité des micro-benchmark.
À partir d'un code de plongée, cela semble être dû à un naïf multiplicateur de la mise en œuvre et aucune accélération matérielle pour le GCM calculs.
Par comparaison AES (de la BCE ou de la mode CBC) dans le JDK 8 utilise un AES-NI accéléré intrinsèque et est (pour Java au moins) très rapide (de l'ordre de 1 GO/s sur le même matériel), mais l'ensemble de l'AES/GCM performance est complètement dominé par l'cassé GCM performance.
Il y a plans de mise en œuvre de l'accélération matérielle, et il y a eu des tiers de soumettre des observations pour améliorer les performances avec, mais ils n'ont pas fait en un instant.
Autre chose d'être conscient, c'est que le JDK GCM mise en œuvre également les tampons de l'ensemble du texte en clair sur le déchiffrement jusqu'à ce que la balise de l'authentification à la fin du texte chiffré est vérifiée, ce qui handicape pour une utilisation avec des messages volumineux.
Château gonflable a (au moment de la rédaction) plus rapide GCM implémentations (et POE si vous écrivez un logiciel open source de pas être accablés par le logiciel de lois sur les brevets).
Mis à jour en juillet 2015 - 1.8.0_45 et JDK 9
JDK 8+ obtiendrez une amélioration (et de la constante de temps), la mise en œuvre Java (contribution de Florian Weimer de RedHat) - ce qui a débarqué dans le JDK 9 EA construit, mais apparemment pas encore en 1.8.0_45.
JDK9 (depuis EA b72 au moins) a également GCM intrinsèques - AES/GCM vitesse sur b72 est 18MB/s sans intrinsèques activé et 25 mo/s avec intrinsèques activé, les deux qui sont décevants, - pour la comparaison la plus rapide (pas de temps constant) BC mise en œuvre est de ~60 MO/s et la plus lente (temps constant, pas entièrement optimisé) est d'environ 26 MO/s.
Mise À Jour Jan 2016 - 1.8.0_72:
Certaines performances corrige atterri dans le JDK 1.8.0_60 et de la performance de la même référence est maintenant 18MO/s - 6x amélioration de l'original, mais encore beaucoup plus lent que la colombie-britannique implémentations.
Cela a maintenant été partiellement abordée dans Java 8u60 avec JDK-8069072. Sans ce correctif-je obtenir 2,5 M/s. Avec ce correctif-je obtenir 25M/s. La désactivation de la GCM complètement me donne 60M/s.
Pour désactiver GCM complètement créer un fichier nommé
java.security
avec la ligne suivante:Ensuite commencer votre processus Java avec:
Si cela ne fonctionne pas, vous devrez peut-être activer primordial de propriétés de sécurité en modifiant
/usr/java/default/jre/lib/security/java.security
(chemin d'accès réel peut être différent selon les OS) et d'ajouter:/dev/urandom
: démarrer avecjava -Djava.security.egd=file:/dev/urandom …
(à partir des commentaires dans<java-root>/jre/lib/security/java.security
).OpenSSL mise en œuvre est optimisée par la assemblée de routine à l'aide de pclmulqdq instruction(plate-forme x86). Il est très rapide en raison de la parallèle de l'algorithme.
L'implémentation de java est lent. mais il a également été optimisé en Hotspot à l'aide de l'assemblée de routine(pas en parallèle). vous avez pour réchauffer la jvm à utiliser Hotspot intrinsèque. La valeur par défaut -XX:CompileThreshold est de 10000.
//pseudocode
warmUp_GCM_cipher_loop10000_times();
do_benchmark();