web-dev-qa-db-fra.com

Java SecureRandom ne bloque pas? Comment?

Je sais par expérience que la lecture à partir de blocs/dev/random lorsque le pool d'entropie du noyau Linux manque d'entropie. En outre, j'ai vu de nombreux articles et entrées de blog indiquant que lors de l'exécution sous Linux, Java.security.SecureRandom utilise/dev/random comme source d'entropie et se bloque donc lorsque le pool d'entropie du noyau est épuisé.

Cependant, je ne suis pas en mesure de produire une expérience qui bloque SecureRandom. Inversement, il semble facile d'obtenir une simple ligne bash qui lit à partir de/dev/random pour bloquer.

Voici le code Java que j'utilise pour ces expériences:

import Java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr = new SecureRandom();
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}

Il génère un peu plus de 1 000 000 d'entiers aléatoires de 32 bits. Cela devrait être 2 ^ (20 + log2 (32)) = 2 ^ 25 bits ou 2 ^ 22 (un peu plus de 4 millions) octets d'entropie, non? Cependant, il ne bloque jamais. Il se termine toujours en environ 1,2 seconde, que je bouge la souris ou non.

Le bash one-liner que j'ai utilisé est:

head -c 100 /dev/random | xxd

Cela se bloque facilement. Tant que je garde ma main hors de la souris et du clavier, elle reste là sans rien faire pendant plusieurs minutes. Et je ne demande que 100 octets d'entropie.

Je manque sûrement quelque chose ici. Quelqu'un pourrait-il expliquer ce qui se passe?

Merci!

22
user1483512

OpenJDK et Sun lisent à partir de /dev/urandom, ne pas /dev/random, au moins sur la machine sur laquelle j'ai testé (OpenJDK JRE 6b27 et Sun JRE 6.26 sur Debian squeeze AMD64). Pour une raison quelconque, ils ouvrent tous les deux /dev/random aussi, mais jamais lu. Les articles de blog que vous avez lus se sont donc trompés ou ont été appliqués à une version différente de la mienne (et, apparemment, la vôtre).

Vous pouvez vérifier si le vôtre lit à partir de /dev/random ou /dev/urandom en le retraçant:

strace -o a.strace -f -e file Java A

et recherchez la partie pertinente de la trace:

21165 open("/dev/random", O_RDONLY)     = 6
…
21165 open("/dev/urandom", O_RDONLY)    = 7
…
21165 read(7, "\322\223\211\262Zs\300\345\3264l\254\354[\6wS\326q@", 20) = 20
…

Ne vous inquiétez pas, /dev/urandom est parfaitement adapté à la cryptographie.

SecureRandom de Java fait utilise/dev/random, mais seulement brièvement.

Plus précisément, il l'utilise uniquement lors de la génération des informations de départ, soit en appelant explicitement SecureRandom.generateSeed() soit par le premier appel à nextInt()

Donc, pour répéter votre exemple bash, vous pouvez faire ce qui suit, et il devrait bloquer.

import Java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr;
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            sr = new SecureRandom();
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}
9
diedthreetimes

Non seulement pour garder un vieux fil vivant, mais certaines personnes ont peut-être manqué une partie importante de la longue histoire derrière cela ... Il s'agit d'un bogue infâme et persistant bien connu lors de l'utilisation de/dev/urandom de Java versions 1.4 à versions 1.7. Voir les liens ci-dessous:

http://bugs.Java.com/view_bug.do?bug_id=6202721

http://bugs.Java.com/view_bug.do?bug_id=470509

Pour cela, je sais, cela a finalement été résolu dans Java 8 comme indiqué par Oracle: https://docs.Oracle.com/javase/8/docs/technotes/guides /security/enhancements-8.html

SHA1PRNG et NativePRNG ont été corrigés pour respecter correctement les propriétés de la source de départ SecureRandom dans le fichier Java.security. (La solution de contournement obscure utilisant file: /// dev/urandom et file:/dev /./ urandom n'est plus requise.)

4
ozys

$Java_HOME/lib/security/Java.security: La propriété securerandom.source Dicte le périphérique à utiliser lors de l'utilisation de SecureRandom. La valeur par défaut est /dev/urandom, Ce qui signifie Java ne bloquera jamais.

Pour les nouveaux venus sur le sujet, /dev/urandom Est une version spéciale de /dev/random, Qui utilisera la véritable entropie de /dev/random Lorsqu'elle sera disponible, puis des données pseudo-aléatoires ensemencées en véritable entropie si elle était disponible .

Dans les environnements sans tête, VM et Cloud, je recommande de semer urandom à partir d'une source externe, telle que http://random.org

3

Sous Linux récent (6 dernières années, disons), la différence de calcul entre/dev/random (blocage) et/dev/urandom (non blocage) est très marginale; l'entropie brute est blanchie via un générateur de nombres aléatoires et les deux appareils utilisent les mêmes bits. La seule différence est que/dev/random a un limiteur de débit qui l'empêche de fonctionner trop longtemps sans réamorçage. L'estimation des taux elle-même est assez discutable.

Ceci est expliqué dans http://www.2uo.de/myths-about-urandom/ qui indique à la maison que/dev/urandom est suffisant pour amorcer vos algorithmes cryptographiques pour les clés de session. Si vous ne l'appelez pas trop souvent, les deux doivent être de qualité identique ou presque identique.

J'ai vu un blocage via/dev/random lors de l'exécution d'un robot Web qui devait établir plus de 1000 connexions SSL simultanées (clés de session), et oui, nous sommes passés à/dev/urandom; pour la génération en vrac de paires de clés publiques/privées de longue date (une situation rare), un dispositif d'entropie personnalisé est préférable.

3
Paul Baclace