web-dev-qa-db-fra.com

Comment résoudre slow Java `SecureRandom`?

Si vous voulez un nombre aléatoire cryptographiquement fort en Java, vous utilisez SecureRandom. Malheureusement, SecureRandom peut être très lent. S'il utilise /dev/random sur Linux, il peut bloquer l’attente d’une entropie suffisante pour se construire. Comment évitez-vous la pénalité de performance?

Quelqu'un at-il utilisé ncommon Maths comme solution à ce problème?

Quelqu'un peut-il confirmer que ce problème de performances a été résolu dans JDK 6?

152
David G

Si vous voulez de vraies données aléatoires, vous devez malheureusement les attendre. Ceci inclut la graine pour un SecureRandom PRNG. Mathématiques peu communes ne peuvent pas collecter des données véritablement aléatoires plus rapidement que SecureRandom, bien qu'ils puissent se connecter à Internet pour télécharger des données de base à partir d'un site Web particulier. Mon hypothèse est qu'il est peu probable que cela soit plus rapide que /dev/random Là où c'est disponible.

Si vous voulez un PRNG, faites quelque chose comme ça:

SecureRandom.getInstance("SHA1PRNG");

Les chaînes prises en charge dépendent du fournisseur SecureRandom SPI, mais vous pouvez les énumérer à l'aide de Security.getProviders() et Provider.getService().

Sun aime beaucoup SHA1PRNG, il est donc largement disponible. Ce n'est pas particulièrement rapide lorsque les GNPA disparaissent, mais les GNRP ne feront que traiter des chiffres, pas pour bloquer la mesure physique de l'entropie.

L’exception est que si vous n’appelez pas setSeed() avant d’obtenir des données, le PRNG s’ensemencera automatiquement la première fois que vous appelez next() ou nextBytes(). Il utilisera généralement une quantité assez faible de données aléatoires provenant du système. Cet appel bloquera, mais rendra votre source de nombres aléatoires bien plus sûre que toute variante de "hash the current temps avec le PID, ajoutez 27, et espérez que tout ira pour le mieux ". Si vous avez simplement besoin de chiffres aléatoires pour un jeu, ou si vous souhaitez que le flux soit reproductible à l'avenir en utilisant la même graine à des fins de test, il est peu sûr. la graine est toujours utile.

72
Steve Jessop

Vous devriez pouvoir sélectionner le fichier/dev/urandom plus rapide, mais légèrement moins sécurisé, sous Linux avec:

-Djava.security.egd=file:/dev/urandom

Toutefois, cela ne fonctionne pas avec Java 5 et version ultérieure ( Java Bug 6202721 ). Le travail suggéré consiste à utiliser:

-Djava.security.egd=file:/dev/./urandom

(notez le supplément /./)

171
Thomas Leonard

Sous Linux, la mise en oeuvre par défaut de SecureRandom est NativePRNG (code source ici ), ce qui a tendance à être très lent. Sous Windows, la valeur par défaut est SHA1PRNG, qui, comme d’autres l’ont souligné, peut également être utilisé sous Linux si vous le spécifiez explicitement.

NativePRNG diffère de SHA1PRNG and Uncommons Maths ' AESCounterRNG en ce sens qu'il reçoit en continu l'entropie du système d'exploitation (en lisant dans /dev/urandom). Les autres PRNG n'acquièrent aucune entropie supplémentaire après l'ensemencement.

AESCounterRNG est environ 10 fois plus rapide que SHA1PRNG, lequel IIRC est lui-même deux ou trois fois plus rapide que NativePRNG.

Si vous avez besoin d'une PRNG plus rapide) qui acquiert l'entropie après l'initialisation, voyez si vous pouvez trouver une implémentation Java de Fortuna . PRNG d'une implémentation Fortuna est identique à celle utilisée par AESCounterRNG, mais il existe également un système sophistiqué de pooling d'entropie et de réensemencement automatique.

34
Dan Dyer

Beaucoup de distributions Linux (principalement basées sur Debian) configurent OpenJDK pour utiliser /dev/random pour entropie.

/dev/random est par définition lent (et peut même bloquer).

À partir de là, vous avez deux options pour le débloquer:

  1. Améliorer l'entropie, ou
  2. Réduire les exigences aléatoires.

Option 1, Améliorer l'entropie

Pour obtenir plus d'entropie dans /dev/random _, essayez le démon haveged . C'est un démon qui collecte continuellement l'entropie HAVEGE et fonctionne également dans un environnement virtualisé, car il ne nécessite aucun matériel particulier, mais uniquement le processeur et une horloge.

Sur Ubuntu/Debian:

apt-get install haveged
update-rc.d haveged defaults
service haveged start

Sur RHEL/CentOS:

yum install haveged
systemctl enable haveged
systemctl start haveged

Option 2. Réduire les exigences relatives au caractère aléatoire

Si, pour une raison quelconque, la solution ci-dessus ne vous aide pas ou si vous ne vous préoccupez pas du caractère aléatoire fort du point de vue de la cryptographie, vous pouvez passer à /dev/urandom à la place, ce qui garantit de ne pas bloquer.

Pour le faire globalement, éditez le fichier jre/lib/security/Java.security par défaut Java pour utiliser /dev/urandom _ (à cause d'un autre bug il doit être spécifié comme /dev/./urandom).

Comme ça:

#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom

Ensuite, vous n'aurez plus jamais à le spécifier sur la ligne de commande.


Remarque: si vous utilisez la cryptographie, vous besoin = Bonne entropie. Exemple - Android PRNG numéro ) réduit la sécurité des portefeuilles Bitcoin.

22
rustyx

J'ai eu un problème similaire avec les appels à SecureRandom bloquant environ 25 secondes à la fois sur un serveur Debian sans tête. J'ai installé le démon haveged pour m'assurer que /dev/random est maintenu chargé, sur les serveurs sans interface graphique, vous avez besoin de quelque chose comme ceci pour générer l'entropie requise. Mes appels à SecureRandom prennent maintenant peut-être des millisecondes.

16
thunder

Si vous voulez un caractère vraiment "cryptographiquement fort", vous avez besoin d'une source d'entropie forte. /dev/random est lent car il doit attendre que les événements système rassemblent l'entropie (lectures sur disque, paquets réseau, mouvements de la souris, pressions sur les touches, etc.).

Une solution plus rapide est un générateur de nombre aléatoire de matériel. Vous en avez peut-être déjà intégré une sur votre carte mère; Consultez la documentation hw_random pour savoir comment déterminer si vous en avez et comment l'utiliser. Le paquetage rng-tools inclut un démon qui alimentera l’entropie générée par le matériel dans /dev/random.

Si un système HRNG n’est pas disponible sur votre système et que vous êtes prêt à sacrifier la force de l’entropie au profit des performances, vous souhaiterez créer un objet PRNG avec les données de /dev/random, et laissons le PRNG faire le gros du travail. Il existe plusieurs PRNG approuvés par le NIST, énumérés dans SP800-9 , qui sont simples à mettre en œuvre.

11
Chris Kite

Le problème que vous avez mentionné à propos de /dev/random n'est pas avec l'algorithme SecureRandom, mais avec la source de caractère aléatoire qu'il utilise. Les deux sont orthogonaux. Vous devriez savoir lequel des deux vous ralentit.

La page de maths peu commune que vous avez liée mentionne explicitement qu’elles ne traitent pas la source de l’aléatoire.

Vous pouvez essayer différents fournisseurs JCE, tels que BouncyCastle, pour voir si leur implémentation de SecureRandom est plus rapide.

Une brève recherche révèle également les correctifs Linux qui remplacent l’implémentation par défaut avec Fortuna. Je n'en sais pas beaucoup plus à ce sujet, mais vous êtes le bienvenu pour enquêter.

Je devrais également mentionner que s'il est très dangereux d'utiliser un algorithme et/ou une source aléatoire mal implémenté SecureRandom, vous pouvez lancer votre propre fournisseur JCE avec une implémentation personnalisée de SecureRandomSpi. Vous devrez passer par un processus avec Sun pour faire signer votre fournisseur, mais le processus est relativement simple. ils ont juste besoin que vous leur envoyiez par fax un formulaire indiquant que vous êtes au courant des restrictions d'exportation imposées par les États-Unis sur les bibliothèques de chiffrement.

5
ykaganovich

Il existe un outil (au moins sur Ubuntu) qui introduira un hasard artificiel dans votre système. La commande est simplement:

rngd -r /dev/urandom

et vous aurez peut-être besoin d'un Sudo à l'avant. Si vous n'avez pas le paquetage rng-tools, vous devrez l'installer. J'ai essayé ça et ça m'a vraiment aidé!

Source: matt vs world

5
David K

Je suis confronté même problème . Après quelques recherches sur Google avec les bons termes de recherche, je suis tombé sur cet article de Nice sur DigitalOcean .

hasged est une solution potentielle sans compromettre la sécurité.

Je ne fais que citer la partie pertinente de l'article ici.

Basé sur le principe HAVEGE, et précédemment sur sa bibliothèque associée, haveged permet de générer un caractère aléatoire en fonction des variations du temps d'exécution du code sur un processeur. Puisqu'il est presque impossible qu'un morceau de code prenne le même temps exact pour s'exécuter, même dans le même environnement sur le même matériel, le moment d'exécuter un ou plusieurs programmes devrait convenir pour créer une source aléatoire. L'implémentation hasged ensemence la source aléatoire de votre système (généralement/dev/random) en utilisant des différences dans le compteur d'horodatage de votre processeur après l'exécution répétée d'une boucle.

Comment installer asged

Suivez les étapes décrites dans cet article. https://www.digitalocean.com/community/tutorials/how-to-setup-addopy-entropy-for-cloud-servers-using-haveged

Je l'ai posté ici

5
so-random-dude

En utilisant Java 8, j’ai constaté que sur Linux, appeler SecureRandom.getInstanceStrong() me donnerait l’algorithme NativePRNGBlocking. Il se bloquerait souvent pendant plusieurs secondes pour générer quelques octets. de sel.

Je suis passé à la demande explicite de NativePRNGNonBlocking à la place, et comme prévu du nom, il n'est plus bloqué. Je n'ai aucune idée de ce que cela implique pour la sécurité. Vraisemblablement, la version non bloquante ne peut pas garantir la quantité d'entropie utilisée.

Mise à jour : Ok, j'ai trouvé cette excellente explication .

En un mot, pour éviter le blocage, utilisez new SecureRandom(). Ceci utilise /dev/urandom, Ce qui ne bloque pas et est aussi sécurisé que /dev/random. Depuis le message: "Le seul moment où vous voudriez appeler/dev/random, c'est au premier démarrage de la machine et lorsque l'entropie ne s'est pas encore accumulée".

SecureRandom.getInstanceStrong() vous donne le RNG le plus puissant absolu, mais son utilisation est sans danger uniquement dans les situations où un bloc de blocage ne vous affectera pas.

4
Lachlan

Utilisez la source aléatoire sécurisée comme source d’initialisation pour un algorithme récurrent; vous pouvez utiliser ensuite un twister Mersenne pour le travail en bloc au lieu de celui de UncommonMath, qui existe depuis un moment et qui a fait ses preuves mieux que les autres

http://en.wikipedia.org/wiki/Mersenne_twister

Assurez-vous d'actualiser de temps en temps l'aléatoire sécurisé utilisé pour l'initialisation. Par exemple, vous pouvez générer un aléatoire sécurisé par client, en utilisant un générateur pseudo-aléatoire mersenne twister par client, en obtenant un degré de randomisation suffisant.

4
Lorenzo Boccaccia

Je ne me suis pas attaqué à ce problème moi-même, mais je créais un fil au début du programme qui essaie immédiatement de générer une graine, puis meurt. La méthode que vous appelez pour les aléas rejoindra ce thread s'il est actif, ainsi le premier appel ne bloque que s'il se produit très tôt dans l'exécution du programme.

2
Nick

Si votre matériel le prend en charge, essayez avec Java Utilitaire RdRand) dont je suis l'auteur.

Elle est basée sur l'instruction RDRAND d'Intel et est environ 10 fois plus rapide que SecureRandom et ne pose aucun problème de bande passante pour la mise en œuvre de gros volumes.


Notez que cette implémentation ne fonctionne que sur les CPU fournissant l'instruction (c'est-à-dire lorsque l'indicateur de processeur rdrand est défini). Vous devez l'instancier explicitement via le constructeur RdRandRandom(); aucun Provider spécifique n'a été implémenté.

2
user2781824

Il semble que vous deviez être plus clair sur vos exigences en matière de GNA. La meilleure exigence en matière de RNG cryptographique (si je comprends bien) serait que, même si vous connaissez l’algorithme utilisé pour les générer, et que vous connaissez tous les nombres aléatoires générés précédemment, vous ne pouvez obtenir aucune information utile sur les nombres aléatoires générés dans le fichier. avenir, sans dépenser une quantité de puissance informatique peu pratique.

Si vous n’avez pas besoin de cette garantie totale d’aléatoire, il existe probablement des compromis de performance appropriés. J'aurais tendance à être d'accord avec réponse de Dan Dyer à propos de AESCounterRNG de Uncommons-Maths, ou Fortuna (l'un de ses auteurs est Bruce Schneier, un expert en cryptographie). Je n'ai jamais utilisé non plus, mais les idées semblent dignes de confiance à première vue.

Je pense que si vous pouviez générer périodiquement une graine aléatoire initiale (par exemple, une fois par jour ou par heure), vous pourriez utiliser un code de flux rapide pour: générer des nombres aléatoires à partir de fragments successifs du flux (si le chiffrement du flux utilise XOR, il suffit alors de transmettre un flux de valeurs NULL ou de saisir directement les XOR)). Le projet eStream d'ECRYPT contient de nombreuses informations utiles, notamment des points de repère de performance, qui ne maintiendraient pas l'entropie entre les instants où vous le reconstitueriez. sur le plan technique, il serait peut-être possible, avec beaucoup de puissance de calcul, de casser le chiffrement du flux et de deviner son état interne pour pouvoir prédire les nombres aléatoires à venir, mais vous devrez décider si ce risque et ses conséquences sont suffisants pour justifier le calcul. coût d'entretien d'entropie.

Edit: voici quelques notes de cours cryptographiques sur RNG J'ai trouvé sur le net des informations qui semblent très pertinentes pour ce sujet.

2
Jason S

Mon expérience ne concerne que l’initialisation lente du PRNG, pas la génération de données aléatoires par la suite. Essayez une stratégie d’initialisation plus rapide. Comme leur création est coûteuse, traitez-la comme un singleton et réutilisez la même instance. S'il y a trop de conflits de threads pour une instance, regroupez-les ou faites-en un thread local.

Ne faites pas de compromis sur la génération de nombres aléatoires. Une faiblesse ici compromet toute votre sécurité.

Je ne vois pas beaucoup de générateurs COTS basés sur la désintégration atomique, mais il existe plusieurs plans pour les installer, si vous avez vraiment besoin de beaucoup de données aléatoires. Un site qui a toujours des choses intéressantes à regarder, y compris HotBits, est Fourmilab. De John Walker

2
erickson

Un autre élément à examiner est la propriété securerandom.source du fichier lib/security/Java.security.

L’utilisation de/dev/urandom plutôt que/dev/random peut présenter un avantage en termes de performances. N'oubliez pas que si la qualité des nombres aléatoires est importante, ne faites pas de compromis qui compromettrait la sécurité.

1
Diastrophism

Vous pouvez essayer le projet Math commun Apache, qui a quelques implémentations d'algorithmes bien connus:

https://commons.Apache.org/proper/commons-math/userguide/random.html

Cependant, soyez prudent avec la performance. Le constructeur par défaut de RandomDataGenerator crée une instance dédiée de Well19937c, c’est une opération très coûteuse.

Selon la documentation, cette classe n'est pas thread-safe, mais si vous pouvez garantir qu'un seul thread accédera à cette classe, vous ne pourrez initialiser qu'une seule instance. par fil.

0
jfcorugedo