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'entropie suffisante pour construire. Comment éviter la pénalité de performance?
Quelqu'un a-t-il utilisé Uncommon Maths comme solution à ce problème?
quelqu'un peut-il confirmer que ce problème de performance a été résolu dans JDK 6?
18 réponses
si vous voulez de vraies données aléatoires, alors malheureusement vous devez attendre. Cela comprend la graine pour un SecureRandom
PRNG. Uncommon Maths ne peut pas recueillir de vraies données aléatoires plus rapidement que SecureRandom
, bien qu'il puisse se connecter à l'internet pour télécharger des données seed à partir d'un site web particulier. À mon avis, il est peu probable que ce soit plus rapide que /dev/random
lorsque c'est disponible.
Si vous voulez un PRNG, faire quelque chose comme ceci:
SecureRandom.getInstance("SHA1PRNG");
les chaînes qui sont supportées dépendent du fournisseur SPI SecureRandom
, mais vous pouvez les énumérer en utilisant Security.getProviders()
et Provider.getService()
.
Sun est fan de SHA1PRNG, il est donc largement disponible. Il n'est pas particulièrement rapide que PRNGs vont, mais PRNGs seront juste des nombres crunching, pas de blocage pour la mesure physique de l'entropie.
l'exception est que si vous n'appelez pas setSeed()
avant d'obtenir des données, alors le PRNG va se semer une fois la première fois que vous appelez next()
ou nextBytes()
. Il le fait habituellement en utilisant une assez petite quantité de vraies données aléatoires du système. Cet appel peut bloquer, mais rendra votre source de nombres aléatoires beaucoup plus sécurisé que n'importe quelle variante de "hachage à l'heure actuelle avec le PID, ajouter 27, et espérer pour le mieux". Si tout ce que vous avez besoin est des nombres aléatoires pour un jeu, Cependant, ou si vous voulez que le flux soit reproductible dans le futur en utilisant la même graine pour des buts de test, une graine non sécurisée est toujours utile.
vous devriez pouvoir sélectionner le plus rapide-mais-légèrement-moins-sûr / dev / urandom sur Linux en utilisant:
-Djava.security.egd=file:/dev/urandom
cependant, cela ne fonctionne pas avec Java 5 et plus tard ( Java Bug 6202721 ). La solution suggérée est d'utiliser:
-Djava.security.egd=file:/dev/./urandom
(noter l'extra /./
)
sur Linux, l'implémentation par défaut pour SecureRandom
est NativePRNG
(code source ici ), ce qui tend à être très lent. Sous Windows, la valeur par défaut est SHA1PRNG
, qui, comme d'autres l'ont souligné, peut aussi être utilisé sous Linux si vous le spécifiez explicitement.
NativePRNG
diffère de SHA1PRNG
et Uncommons Maths " Aescounterng en ce qu'il reçoit entropie continue du système d'exploitation (par la lecture de /dev/urandom
). Les autres RPNE n'acquièrent pas d'entropie supplémentaire après l'ensemencement.
AESCounterRNG est d'environ 10x plus rapide que le SHA1PRNG
, qui IIRC est lui-même deux ou trois fois plus rapide que le NativePRNG
.
si vous avez besoin d'un PRNG plus rapide qui acquiert entropy après l'initialisation, voyez si vous pouvez trouver une implémentation Java de Fortuna . Le noyau PRNG d'une mise en oeuvre de Fortuna est identique à celui utilisé par contre, il existe aussi un système sophistiqué de mise en commun de l'entropie et de réensemencement automatique.
j'ai eu un problème similaire avec les appels à SecureRandom
bloquant pendant 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 en place, sur les serveurs sans tête, vous avez besoin de quelque chose comme ça pour générer l'entropie requise.
Mes appels à SecureRandom
prennent peut-être des millisecondes.
beaucoup de distributions Linux (principalement basées sur Debian) configurent OpenJDK pour utiliser /dev/random
pour l'entropie.
/dev/random
est, par définition, lent (et peut même bloquer).
D'ici vous avez deux options sur la façon de le débloquer:
- améliorer l'entropie, ou
- réduire les exigences d'aléatoire.
Option 1, Améliorer l'entropie
pour obtenir plus d'entropie dans /dev/random
, essayez le haveged daemon. C'est un démon qui collecte continuellement L'entropie de HAVEGE, et qui fonctionne aussi dans un environnement virtualisé parce qu'il ne nécessite pas de matériel spécial, seulement le CPU lui-même 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 de randomisation
si pour une raison quelconque la solution ci-dessus n'aide pas ou vous ne vous souciez pas cryptographiquement forte aléatoire, vous pouvez passer à /dev/urandom
à la place, qui est garanti de ne pas bloquer.
pour le faire globalement, éditez le fichier jre/lib/security/java.security
dans votre installation Java par défaut pour utiliser /dev/urandom
(en raison d'un autre bug il doit être spécifié comme /dev/./urandom
).
comme ceci:
#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
alors vous n'aurez jamais à le spécifier sur la ligne de commande.
Note: Si vous faites de la cryptographie, vous besoin bonne entropie. Affaire en question - android PRNG issue réduit la sécurité des portefeuilles Bitcoin.
si vous voulez vraiment un aléatoire "cryptographiquement fort", alors vous avez besoin d'une source d'entropie forte. /dev/random
est lent parce qu'il doit attendre que les événements du système rassemblent l'entropie (lectures du disque, paquets réseau, mouvement de la souris, presses à clés, etc.).
une solution plus rapide est un générateur de nombres aléatoires matériel. Vous pouvez déjà avoir un intégré à votre carte mère; consultez la hw_random documentation pour des instructions sur la façon de déterminer si vous l'avez, et comment l'utiliser. Le paquet rng-tools inclut un démon qui alimentera l'entropie générée par le matériel dans /dev/random
.
si un HRNG n'est pas disponible sur votre système, et que vous êtes prêt à sacrifier la force d'entropie pour la performance, vous voudrez ensemencer un bon PRNG avec des données de /dev/random
, et laisser le PRNG faire la majeure partie du travail. Il y a plusieurs NGRP approuvés par le NIST qui sont énumérés dans SP800-90 qui sont simples à mettre.
utilisez l'aléatoire sécurisé comme source d'initialisation pour un algorithme récurrent; vous pourriez utiliser alors un Mersenne twister pour le travail en vrac au lieu de celui dans UncommonMath, qui a été autour pendant un certain temps et s'est avéré mieux que d'autres prng
http://en.wikipedia.org/wiki/Mersenne_twister
assurez-vous d'actualiser maintenant et puis secure aléatoire utilisé pour l'initialisation, par exemple, vous pourriez avoir sécurisé aléatoire généré par client, en utilisant un pseudo générateur aléatoire Mersenne twister par client, en obtenant un degré assez élevé de randomisation
le problème que vous avez mentionné à propos de /dev/random
n'est pas avec l'algorithme SecureRandom
, mais avec la source de l'aléatoire qu'il utilise. Les deux sont orthogonaux. Tu devrais trouver lequel des deux te ralentit.
la page de mathématiques hors du commun que vous avez liée mentionne explicitement qu'elle ne s'adresse pas à la source de l'aléatoire.
vous pouvez essayer différents fournisseurs JCE, tels que BouncyCastle, pour voir si leur mise en œuvre de SecureRandom
est plus rapide.
a brief search révèle également des correctifs Linux qui remplacent L'implémentation par défaut avec Fortuna. Je n'en sais pas plus, mais vous pouvez enquêter.
je dois également mentionner que bien qu'il soit très dangereux d'utiliser un algorithme mal implémenté SecureRandom
et/ou une source aléatoire, vous pouvez lancer votre propre fournisseur JCE avec une implémentation personnalisée de SecureRandomSpi
. Vous aurez besoin de pour passer par un processus avec Sun pour faire signer votre fournisseur, mais c'est en fait assez simple; ils ont juste besoin de vous pour leur faxer un formulaire indiquant que vous êtes au courant des restrictions D'exportation US sur les bibliothèques crypto.
mon expérience a été seulement avec l'initialisation lente du PRNG, pas avec la génération de données aléatoires après cela. Essayez une stratégie d'initialisation plus dynamique. Puisqu'ils sont coûteux à créer, traitez-le comme un singleton et réutilisez la même instance. S'il y a trop de discussion de thread pour une instance, mettez-les en commun ou faites-les thread-local.
ne compromettez pas la génération de nombres aléatoires. Une faiblesse là-bas compromet toute votre sécurité.
Je ne vois pas beaucoup de générateurs à désintégration atomique, mais il y a plusieurs plans là-bas pour eux, 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.
j'ai fait face à de même problème . Après quelques Googling avec les bons termes de recherche, je suis tombé sur cet article agréable sur DigitalOcean .
haveged est une solution potentielle sans compromettre la sécurité.
je cite simplement la partie pertinente de l'article ici.
basée sur le principe de HAVEGE, et précédemment basée sur son association bibliothèque, haveged permet de générer de l'aléatoire basé sur des variations temps d'exécution du code sur un processeur. Puisque c'est presque impossible pour un morceau de code à exécuter exactement au même moment, même dans le même environnement sur le même matériel, le moment de l'exécution d'un seul ou plusieurs programmes devraient convenir pour semer une source aléatoire. Le haveged implementation seeds 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 (TSC) après exécution d'une boucle à plusieurs reprises
comment installer haveged
suivez les étapes de cet article. https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged
Je l'ai posté ici
il semble que vous devriez être plus clair au sujet de vos exigences RNG. La plus forte exigence de RNG cryptographique (comme je le comprends) serait que même si vous connaissez l'algorithme utilisé pour les générer, et vous savez tous les nombres aléatoires précédemment générés, vous ne pourriez pas obtenir des informations utiles sur l'un des nombres aléatoires générés dans l'avenir, sans dépenser une quantité impraticable de puissance de calcul.
si vous n'avez pas besoin de cette garantie le hasard alors il y a probablement des compromis appropriés de performance. J'aurais tendance à être d'accord avec la réponse de Dan Dyer sur Aescounterng de Uncommons-Maths, ou Fortuna (un de ses auteurs est Bruce Schneier, un expert en cryptographie). Je n'ai jamais utilisé l'un ou l'autre mais les idées semblent de bonne réputation à première vue.
je penser que si vous pouviez générer une valeur aléatoire initiale périodiquement (par exemple, une fois par jour ou à l'heure ou quoi qu'il en soit, vous pouvez utiliser un chiffrement de flux rapide pour générer des nombres aléatoires à partir de morceaux successifs du flux (si le chiffrement de flux utilise XOR puis passer simplement dans un flux de nulls ou saisir les bits de XOR directement). Le projet eStream D'ECRYPT contient beaucoup de bonnes informations, y compris des critères de performance. Ce ne serait pas maintenir l'entropie entre les points dans le temps que vous reconstituer, donc si quelqu'un connaissait un des nombres aléatoires et de l'algorithme utilisé, techniquement, il pourrait être possible, avec beaucoup de puissance de calcul, de briser le chiffrement de flux et de deviner son état interne pour être en mesure de prédire les nombres aléatoires futurs. Mais vous devez décider si ce risque et ses conséquences sont suffisantes pour justifier le coût du maintien de l'entropie.
Edit: voici quelques de cryptographie notes de cours sur RNG que j'ai trouvé sur le net qui ont l'air très pertinent à ce sujet.
Il ya un outil (sur Ubuntu au moins) qui alimentera aléatoire artificiel dans votre système. La commande est simple:
rngd -r /dev/urandom
et vous pourriez avoir besoin d'un sudo à l'avant. Si vous n'avez pas de paquet rng-tools, vous devrez l'installer. J'ai essayé ça, et ça m'a vraiment aidé!
Source: matt vs world
en utilisant Java 8, j'ai trouvé que sur Linux appeler SecureRandom.getInstanceStrong()
me donnerait l'algorithme NativePRNGBlocking
. Cela bloquerait souvent pendant plusieurs secondes pour générer quelques octets de sel.
je suis passé à demander explicitement pour NativePRNGNonBlocking
à la place, et comme prévu du nom, il n'est plus Bloqué. Je n'ai aucune idée de ce que les répercussions sur la sécurité de ce sont. Probablement la version non-bloquante ne peut pas garantir la quantité d'entropie utilisée.
Update : Ok, j'ai trouvé cette excellente explication .
en un mot, pour éviter le blocage, utilisez new SecureRandom()
. Cela utilise /dev/urandom
, qui ne bloque pas et est fondamentalement aussi sûr que /dev/random
. Du post: "le seul moment où vous voudriez appeler / dev / random est quand la machine démarre, et que l'entropie ne s'est pas encore accumulée".
SecureRandom.getInstanceStrong()
vous donne le RNG le plus fort absolu, mais il est seulement sûr à utiliser dans les situations où un tas de blocage ne vous affectera pas.
Je ne me suis pas attaqué à ce problème moi-même, mais je produirais un thread au démarrage du programme qui essaye immédiatement de générer une graine, puis meurt. La méthode que vous appelez pour randoms se joindra à ce thread s'il est vivant donc le premier appel ne bloque que s'il se produit très tôt dans l'exécution du programme.
autre chose à examiner est la propriété securerandom.source dans le fichier lib/security / java.sécurité
l'utilisation de /dev/urandom au lieu de /dev/random peut présenter un avantage sur le plan de la performance. Rappelez-vous que si la qualité des nombres aléatoires est importante, ne faites pas de compromis qui brise la sécurité.
si votre matériel le supporte, essayez L'utilitaire Java RdRand disponible à: http://code.google.com/p/lizalab-rdrand-util/
est basé sur l'instruction Rdrand D'Intel et est environ 10 fois plus rapide que SecureRandom et aucun problème de bande passante pour la mise en œuvre de gros volumes.
divulgation Complète, je suis l'auteur de l'utilitaire.
vous pouvez essayer Apache commons Math project, 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
, qui 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 pouvez initialiser une seule instance par Thread.
énoncé du problème
Cette bibliothèque msprandom explique une technique de génération de nombres aléatoires pour les fins sans matériel générateurs. Le cryptage et la signature nécessitent un nombre aléatoire de bonne qualité. Générer des nombres aléatoires (ou des séquences d'octets aléatoires) sans générateurs matériels n'est pas une tâche triviale. En particulier, ce problème est réel pour un petit appareil où les sources de les données sont absentes ou limitées. La solution est d'avoir de vraies graines aléatoires sauvegardées dans un fichier sécurisé (voûte) et un chiffrement qui peut produire des séquences cryptées pseudo générées au hasard (PRNG) basées sur des graines aléatoires avec de bonnes caractéristiques aléatoires.
de nombreuses bibliothèques cryptographiques (par exemple BouncyCastle) utilisent la classe SecureRandom pour le cryptage et la signature pour obtenir des nombres aléatoires. SecureRandom dépend de la mise en œuvre du système D'exploitation. En d'autres termes, la réalisation de random engine est en dehors de votre application que vous ne pouvez pas contrôler. Pour éviter d'utiliser des nombres aléatoires pauvres vous devez seed générateur SecureRandom avec de bonnes données aléatoires chaque fois que vous appelez fonctions cryptographiques qui nécessite les données aléatoires. Ou vous pouvez étendre la classe SecureRandom avec votre réalisation qui produit un nombre aléatoire dont la qualité vous pouvez contrôler.
Idée
nous devons utiliser de vraies données aléatoires stockées dans une voûte de données sécurisée.
quelques étapes comment msprandom dans votre application:
- générez sur votre ordinateur ou bloc-notes une véritable graine aléatoire et mettez-la dans une voûte en utilisant cette bibliothèque.
- mettez une voûte (fichier) avec des graines aléatoires sur votre appareil, ordinateur ou serveur où vous avez besoin de crypter et signer des données.
- chargez la voûte une fois au début du programme lorsque vous avez besoin de chiffrer ou de signer des données.
- appeler la fonction gen-rand de la bibliothèque msprandom pour obtenir des octets aléatoires autant de fois que vous avez besoin.
la chambre forte avec des semences aléatoires est cryptée et sécurisée avec HMAC. Random seed dans une chambre forte est mis à jour chaque fois que vous chargez la chambre forte de manière imprévisible, donc HMAC change aussi. Changer une chambre forte est fait intentionnellement contre la situation si attaquant peut riche une certaine copie de votre chambre forte dans le passé.
True générateur de données aléatoire
pour générer une véritable graine aléatoire, un input humain est utilisé dans msprandom . Voici l'algorithme de collecte de données aléatoires:
- nous exécutons le fil séparé où le compteur atomique augmente chaque tic de 0..255 avec une vitesse très élevée.
- attendre la pression de la clé non tamponnée par l'homme et obtenir un code de scan de bouton pressé.
- Prendre actuelle valeur de nanosecondes à partir du début de L'époque et prendre mod 256 pour convertir sa valeur en octet aléatoire.
- valeurs Xor entre elles: code scan-byte ^ courant-counter-value ^ nanosecondes pour produire un byte aléatoire.
- Ajouter un octet aléatoire au vecteur de sortie. Nous supposons que seulement 3 bits ont un vrai caractère aléatoire dans cet octet aléatoire. Donc, pour obtenir de véritables 32 octets aléatoires nous avons besoin ~ 32 * 3 bouton presser de l'entrée de l'utilisateur.
- répétez les étapes 2-5 jusqu'à ce que nous obtenions quantité requise d'octets aléatoires. Si nous avons recueilli la quantité requise de données aléatoires, puis faire étape finale -> vecteur de sortie de hachage avec cryptographiquement forte fonction de hachage pour garantir que la probabilité 1 et 0 bits dans le vecteur de sortie sera de 0,5. Notez, que la fonction de hachage utilisé ici seulement pour mélanger des bits aléatoires et ne pas influencer la qualité des données aléatoires. Afin de hachage(données aléatoires) = données aléatoires. En utilisant cet algorithme le msprandom recueille un 512 bits aléatoires vrais comme une graine qui sera sauvée une voûte.
pourquoi 512 bits aléatoires est suffisant?
Eh bien, chaque PRNG a besoin d'une vraie graine aléatoire. Si un attaquant connaît une graine alors il peut prédire la génération de clés et ainsi de suite. 256 bits de semence initiale aléatoire est assez loin pour garder des secrets de qualité millitaire. J'ai fait 512 pour être sûr que personne ne peut forcer ou deviner la graine aléatoire initiale. Ainsi, vous pouvez librement utiliser msprandom pour ensemencer vos générateurs PRNG ou SecureRandom.