Si vous voulez des nombres aléatoires cryptographiquement forts en Java, vous utilisez SecureRandom
. Malheureusement, cela SecureRandom
peut être très lent. S'il utilise /dev/random
Linux, il peut bloquer l'attente d'une entropie suffisante pour s'accumuler. Comment éviter la pénalité de performance?
Quelqu'un a-t-il utilisé des mathématiques peu communes comme solution à ce problème?
Quelqu'un peut-il confirmer que ce problème de performances a été résolu dans JDK 6?
Réponses:
Si vous voulez de vraies données aléatoires, vous devez malheureusement l'attendre. Cela inclut la graine pour un
SecureRandom
PRNG. Uncommon Maths ne peut pas collecter de vraies données aléatoires plus rapidement queSecureRandom
, bien qu'il puisse se connecter à Internet pour télécharger des données de départ à partir d'un site Web particulier. Je suppose que cela ne sera probablement pas plus rapide que/dev/random
là où c'est disponible.Si vous voulez un PRNG, faites quelque chose comme ceci:
Les chaînes prises en charge dépendent du
SecureRandom
fournisseur SPI, mais vous pouvez les énumérer à l'aide deSecurity.getProviders()
etProvider.getService()
.Sun aime SHA1PRNG, donc il est largement disponible. Ce n'est pas particulièrement rapide car les PRNG vont, mais les PRNG ne feront que croquer des nombres, pas bloquer pour la mesure physique de l'entropie.
L'exception est que si vous n'appelez pas
setSeed()
avant d'obtenir des données, le PRNG se déclenchera une fois la première fois que vous appeleznext()
ounextBytes()
. Il le fera généralement 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ée que n'importe quelle variante de "hacher l'heure actuelle avec le PID, ajouter 27 et espérer le meilleur". Si tout ce dont vous avez besoin, ce sont des nombres aléatoires pour un jeu, ou si vous voulez que le flux soit répétable à l'avenir en utilisant la même graine à des fins de test, une graine non sécurisée est toujours utile.la source
Vous devriez pouvoir sélectionner le plus rapide mais légèrement moins sécurisé / dev / urandom sous Linux en utilisant:
Cependant, cela ne fonctionne pas avec Java 5 et versions ultérieures ( bogue Java 6202721 ). La solution suggérée consiste à utiliser:
(notez le supplément
/./
)la source
/dev/urandom
, Sun le traite comme une chaîne magique et l'utilise de/dev/random
toute façon, vous devez donc le simuler. Quand unefile:
URL n'est-elle pas unefile:
URL? Chaque fois que Sun décide que ce n'est pas le cas :-(file:/dev/urandom
set in-Djava.security.egd
ou insecurerandom.source
dans le fichier java.security,/dev/random/
soit toujours lu à chaque foisSecureRandom.getSeed()
(ousetSeed()
est appelé). La solution de contournement avec lesfile:/dev/./urandom
résultats en ne lisant pas/dev/random
du tout (confirmé avec strace)/dev/urandom
n'est pas moins sécurisé que/dev/random
lorsqu'il est implémenté avec un CSPRNG moderne: en.wikipedia.org/wiki//dev/random#FreeBSD/dev/urandom/
est ce qui se passe si vous l'utilisez pour générer des secrets sur un nouveau matériel prêt à l'emploi, qui pourrait être dans un état tout à fait prévisible./dev/urandom/
ne bloquera pas l'entropie même si c'est un cas où vous devriez. La situation est encore pire si le secret est persistant, comme si la première chose que votre appareil fait au premier démarrage est de générer une paire de clés publique-privée. En dehors de ces situations effrayantes, un bien/dev/urandom
vaut mieux que d'utiliser lesSecureRandom
algorithmes courants de toute façon.Sous Linux, l'implémentation par défaut de
SecureRandom
estNativePRNG
(code source ici ), qui a tendance à être très lente. Sous Windows, la valeur par défaut estSHA1PRNG
, ce que, comme d'autres l'ont souligné, vous pouvez également utiliser sous Linux si vous le spécifiez explicitement.NativePRNG
diffère de AESCounterRNG deSHA1PRNG
Uncommons Maths en ce qu'il reçoit continuellement l'entropie du système d'exploitation (en lisant à partir de ). Les autres PRNG n'acquièrent aucune entropie supplémentaire après l'ensemencement./dev/urandom
AESCounterRNG est environ 10 fois plus rapide que
SHA1PRNG
, ce que l'IIRC est lui-même deux ou trois fois plus rapide queNativePRNG
.Si vous avez besoin d'un PRNG plus rapide qui acquiert de l'entropie après l'initialisation, voyez si vous pouvez trouver une implémentation Java de Fortuna . Le PRNG de base d'une implémentation Fortuna est identique à celui utilisé par AESCounterRNG, mais il existe également un système sophistiqué de regroupement d'entropie et de réensemencement automatique.
la source
De nombreuses distributions Linux (principalement basées sur Debian) configurent OpenJDK à utiliser
/dev/random
pour l'entropie./dev/random
est par définition lent (et peut même bloquer).De là, vous avez deux options pour le débloquer:
Option 1, améliorer l'entropie
Pour obtenir plus d'entropie
/dev/random
, essayez le démon hasged . C'est un démon qui collecte en permanence l'entropie HAVEGE, et fonctionne également dans un environnement virtualisé car il ne nécessite aucun matériel spécial, seulement le processeur lui-même et une horloge.Sur Ubuntu / Debian:
Sur RHEL / CentOS:
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 souciez pas du hasard cryptographique fort, vous pouvez passer à la
/dev/urandom
place, ce qui est garanti de ne pas bloquer.Pour le faire globalement, modifiez le fichier
jre/lib/security/java.security
dans votre installation Java par défaut à utiliser/dev/urandom
(en raison d'un autre bogue, il doit être spécifié comme/dev/./urandom
).Comme ça:
Ensuite, vous n'aurez plus jamais à le spécifier sur la ligne de commande.
Remarque: si vous faites de la cryptographie, vous avez besoin d'une bonne entropie. Affaire au point - problème android PRNG réduit la sécurité des portefeuilles Bitcoin.
la source
/dev/random
est par définition lent (et peut même bloquer)" est faux; cela dépend entièrement de la configuration du système. Les machines plus récentes peuvent avoir par exemple un RNG rapide dans le CPU qui peut être utilisé, et les machines BSD ont généralement la même implémentation pour/dev/random
et/devl/urandom
. Pourtant, vous ne devriez probablement pas compter sur la/dev/random
rapidité, nécessairement. Sur les machines virtuelles, vous souhaiterez peut-être installer l'ensemble d'outils client sur la machine virtuelle cliente afin qu'elle puisse utiliser le RNG du système d'exploitation hôte.J'ai eu un problème similaire avec les appels au
SecureRandom
blocage pendant environ 25 secondes à la fois sur un serveur Debian sans tête. J'ai installé lehaveged
démon pour m'assurer qu'il/dev/random
reste à jour, sur les serveurs sans tête, vous avez besoin de quelque chose comme ça pour générer l'entropie requise. Mes appels àSecureRandom
maintenant prennent peut-être des millisecondes.la source
Si vous voulez un caractère aléatoire 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 de disque, paquets réseau, mouvement de la souris, pressions de touches, etc.).Une solution plus rapide est un générateur de nombres aléatoires matériel. Vous en avez peut-être déjà un intégré à votre carte mère; consultez la documentation hw_random pour savoir si vous l'avez et comment l'utiliser. Le paquet rng-tools comprend un démon qui alimentera l'entropie générée par le matériel
/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 semer un bon PRNG avec des données de
/dev/random
, et laisser le PRNG faire le gros du travail. Il existe plusieurs PRNG approuvés par le NIST répertoriés dans SP800-90 qui sont simples à mettre en œuvre.la source
En utilisant Java 8, j'ai trouvé que sur Linux, l'appel
SecureRandom.getInstanceStrong()
me donnerait l'NativePRNGBlocking
algorithme. Cela bloquait souvent pendant plusieurs secondes pour générer quelques octets de sel.Je suis passé à demander explicitement à la
NativePRNGNonBlocking
place, et comme prévu par le 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 de bloquer, utilisez
new SecureRandom()
. Cela utilise/dev/urandom
, qui ne bloque pas et est fondamentalement aussi sécurisé que/dev/random
. Extrait du message: "La seule fois où vous voudriez appeler / dev / random, c'est lorsque la machine démarre pour la première fois et que l'entropie ne s'est pas encore accumulée".SecureRandom.getInstanceStrong()
vous donne le RNG le plus puissant absolu, mais il n'est sûr à utiliser que dans les situations où un tas de blocages ne vous affectera pas.la source
getInstanceStrong()
que les clés à long terme, telles que celles des certificats TLS. Et même dans ce cas, je préférerais utilisernew SecureRandom()
un générateur de paires de clés compatible FIPS ou un générateur de nombres aléatoires. Alors oui, cela fournit une réponse, si/dev/urandom
cela ne bloque pas: en fin de compte, il repose toujours sur l'entropie du système après tout; mais c'est un très bon conseil en général . Si/dev/urandom
vous bloquez, vous devrez peut-être corriger la source du problème plutôt que votre application Java.Il existe un outil (au moins sur Ubuntu) qui alimentera votre système de manière aléatoire artificielle. La commande est simplement:
et vous aurez peut-être 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 monde
la source
sudo rngd -r /dev/urandom
avecsudo apt install rng-tools
in xenialJ'ai fait face au même problème . Après quelques recherches sur Google avec les bons termes de recherche, je suis tombé sur ce bel article sur DigitalOcean .
haveged est une solution potentielle sans compromis sur la sécurité.
Je cite simplement la partie pertinente de l'article ici.
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
la source
Le problème auquel vous avez fait référence
/dev/random
n'est pas lié à l'SecureRandom
algorithme, mais à la source du caractère aléatoire qu'il utilise. Les deux sont orthogonaux. Vous devez déterminer lequel des deux vous ralentit.La page de mathématiques inhabituelles que vous avez liée mentionne explicitement qu'elles ne traitent pas la source du caractère aléatoire.
Vous pouvez essayer différents fournisseurs JCE, tels que BouncyCastle, pour voir si leur implémentation
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 invités à enquêter.
Je dois également mentionner que s'il est très dangereux d'utiliser un
SecureRandom
algorithme mal implémenté et / ou une source d'aléatoire, vous pouvez déployer votre propre fournisseur JCE avec une implémentation personnalisée deSecureRandomSpi
. Vous devrez passer par un processus avec Sun pour obtenir la signature de votre fournisseur, mais c'est en fait assez simple; ils ont juste besoin que vous leur faxiez un formulaire indiquant que vous êtes au courant des restrictions d'exportation américaines sur les bibliothèques de cryptographie.la source
Utilisez l'aléatoire sécurisé comme source d'initialisation pour un algorithme récurrent; vous pouvez alors utiliser un twister Mersenne pour le travail en vrac au lieu de celui de UncommonMath, qui existe depuis un certain temps et qui s'est avéré meilleur que les autres prng
http://en.wikipedia.org/wiki/Mersenne_twister
Assurez-vous de rafraîchir de temps en temps le aléatoire sécurisé utilisé pour l'initialisation, par exemple, vous pourriez avoir un aléatoire sécurisé généré par client, en utilisant un générateur pseudo aléatoire mersenne twister par client, obtenant un degré de randomisation suffisamment élevé
la source
Random
, mais pas pourSecureRandom
.Selon la documentation , les différents algorithmes utilisés par SecureRandom sont, par ordre de préférence:
Depuis que vous avez posé des questions sur Linux, j'ignore l'implémentation de Windows, ainsi que SunPKCS11 qui n'est vraiment disponible que sur Solaris, sauf si vous l'avez installé vous-même - et alors vous ne poseriez pas cette question.
Selon cette même documentation, ce que ces algorithmes utilisent sont
SHA1PRNG
L'amorçage initial est actuellement effectué via une combinaison d'attributs système et du périphérique de collecte d'entropie java.security.
NativePRNG
nextBytes()
utilise des/dev/urandom
generateSeed()
utilisations/dev/random
NativePRNGBlocking
nextBytes()
etgenerateSeed()
utilisation/dev/random
NativePRNGNonBlocking
nextBytes()
etgenerateSeed()
utilisation/dev/urandom
Cela signifie que si vous utilisez
SecureRandom random = new SecureRandom()
, il descend dans cette liste jusqu'à ce qu'il en trouve une qui fonctionne, qui sera généralement NativePRNG. Et cela signifie qu'il se lance à partir de/dev/random
(ou l'utilise si vous générez explicitement une graine), puis utilise/dev/urandom
pour obtenir les octets suivants, entiers, doubles, booléens, what-have-yous.Puisqu'il
/dev/random
bloque (il bloque jusqu'à ce qu'il ait suffisamment d'entropie dans le pool d'entropie), cela peut nuire aux performances.Une solution à cela consiste à utiliser quelque chose comme avoir pour générer suffisamment d'entropie, une autre solution utilise à la
/dev/urandom
place. Bien que vous puissiez définir cela pour l'ensemble de jvm, une meilleure solution consiste à le faire pour cette instance spécifique deSecureRandom
, en utilisantSecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking")
. Notez que cette méthode peut lever une NoSuchAlgorithmException si NativePRNGNonBlocking, alors soyez prêt à revenir à la valeur par défaut.Notez également que sur d'autres systèmes * nix,
/dev/urandom
peut se comporter différemment .Est-ce
/dev/urandom
assez aléatoire?La sagesse conventionnelle veut que ce
/dev/random
soit assez aléatoire. Cependant, certaines voix diffèrent. Dans «La bonne façon d'utiliser SecureRandom» et «Mythes sur / dev / urandom» , on soutient que/dev/urandom/
c'est tout aussi bien.Les utilisateurs de la pile de sécurité de l'information sont d'accord avec cela . Fondamentalement, si vous devez demander,
/dev/urandom
c'est bien pour votre objectif.la source
Je n'ai pas rencontré ce problème moi-même, mais je créerais un thread au démarrage du programme qui essaie 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 actif, donc le premier appel ne se bloque que s'il se produit très tôt dans l'exécution du programme.
la source
Mon expérience a été uniquement avec une initialisation lente du PRNG, pas avec la génération de données aléatoires par la suite. Essayez une stratégie d'initialisation plus rapide. 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 conflits de threads pour une instance, regroupez-les ou rendez-les thread-locaux.
Ne faites pas de compromis sur 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 basés sur la désintégration atomique COTS, mais il existe plusieurs plans 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.
la source
SecureRandom
a changé plusieurs fois au cours des 10 dernières années dans sa collecte d'entropie.SecureRandom
soit toujours un problème, mais une faible entropie dans un système sera toujours un problème. L'utilisation d'un singleton créera du code fortement couplé, qui est un anti-modèle de conception. Il doit donc être utilisé avec une extrême prudence; vous devriez de préférence inverser toutes les références dans le code si vous voulez résoudre le problème.Il semble que vous devriez être plus clair sur vos exigences RNG. L'exigence RNG cryptographique la plus forte (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 pourriez pas obtenir d'informations utiles sur l'un des nombres aléatoires générés dans le futur, sans dépenser une quantité irréalisable de puissance de calcul.
Si vous n'avez pas besoin de cette garantie complète du caractère aléatoire, il existe probablement des compromis de performance appropriés. J'aurais tendance à être d'accord avec la réponse de Dan Dyer sur AESCounterRNG d'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 réputées à première vue.
Je penserais que si vous pouviez générer une graine aléatoire initiale périodiquement (par exemple une fois par jour ou heure ou autre), vous pourriez 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 alors juste passer un flux de valeurs nulles ou saisir directement les bits XOR). ECRYPTEEStream projet a beaucoup de bonnes informations, y compris des repères de performance. Cela ne maintiendrait pas l'entropie entre les points dans le temps où vous le remplissez, donc si quelqu'un connaissait l'un des nombres aléatoires et l'algorithme que vous avez utilisé, techniquement, il pourrait être possible, avec beaucoup de puissance de calcul, de casser le chiffrement du flux et devinez son état interne pour pouvoir prédire les futurs nombres aléatoires. Mais vous devrez décider si ce risque et ses conséquences sont suffisants pour justifier le coût du maintien de l'entropie.
Edit: en voici quelques notes de cours cryptographiques sur RNG que j'ai trouvées sur le net qui semblent très pertinentes pour ce sujet.
la source
Si votre matériel le prend en charge, essayez d' utiliser l'utilitaire Java RdRand dont je suis l'auteur.
Il est basé sur les
RDRAND
instructions d'Intel et est environ 10 fois plus rapideSecureRandom
et sans problème de bande passante pour la mise en œuvre de gros volumes.Notez que cette implémentation ne fonctionne que sur les CPU qui fournissent l'instruction (c'est-à-dire lorsque le
rdrand
drapeau du processeur est défini). Vous devez l'instancier explicitement via leRdRandRandom()
constructeur; aucun spécifiqueProvider
n'a été mis en œuvre.la source
RDRAND
c'est une bonne source, mais c'est un peu indigne de confiance. Il doit certainement être une entrée parmi tant d'autres dans un collectionneur (sans offenser David Johnston).Il faut également examiner la propriété securerandom.source dans le fichier lib / security / java.security
Il peut y avoir un avantage en termes de performances à utiliser / dev / urandom plutôt que / dev / random. N'oubliez pas que si la qualité des nombres aléatoires est importante, ne faites pas de compromis qui brise la sécurité.
la source