Service lent Temps de réponse: Java SecureRandom & /dev/random

j'essaie de déboguer quelques réponses lentes servies par une application déployée sur Tomcat. En ce moment, je me concentre sur SecureRandom et /dev/random (certaines des autres causes probables ont été étudiées et écartées). Le schéma est le suivant:

  • le premier appel prend exactement 30.0 XY secondes après le redémarrage de Tomcat (même si la requête arrive 4 minutes après le démarrage)
  • plus tard, certains appels prennent exactement 15.0 PQ secondes (il n'y avait aucun motif spécifique que je pouvais établir, pq étant le temps approximatif pris dans TP99)

l'appel de service implique le chiffrement et le déchiffrement ( AES/ECB/PKCS5Padding ).

Est-il possible que SecureRandom init/repeuplement conduit à cela?

(bien que, là est un journal écrit dans catalina.le journal qui dit "Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [28,760] milliseconds." )

aussi, pour vérifier si /dev/random ou /dev/urandom est utilisé, j'ai utilisé le test de cette question . À ma grande surprise, je n'ai pas vu les lectures de l'un ou l'autre d'entre eux contrairement à la façon dont il se passe dans la question liée. Ce sont les dernières lignes du strace log:

3561  lstat("/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/jsse.jar", {st_mode=S_IFREG|0644, st_size=258525, ...}) = 0
3561  open("/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/jsse.jar", O_RDONLY) = 6
3561  stat("/dev/random", {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
3561  stat("/dev/urandom", {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
3561  open("/dev/random", O_RDONLY)     = 7
3561  open("/dev/urandom", O_RDONLY)    = 8
3561  unlink("/tmp/hsperfdata_xxxx/3560") = 0

Qu'est-ce qui est alors utilisé pour ensemencer la garantie?

pour info, java -version

java version "1.6.0_32"
OpenJDK Runtime Environment (IcedTea6 1.13.4) (rhel-7.1.13.4.el6_5-x86_64)
OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode)
5
demandé sur Community 2014-11-27 18:14:01

2 réponses

Je ne pouvais pas vérifier votre version en béton OpenJDK, mais je pouvais vérifier jdk6-B33 .

SecureRandom utilise SeedGenerator pour obtenir les graines d'octets

public byte[] engineGenerateSeed(int numBytes) {
    byte[] b = new byte[numBytes];
    SeedGenerator.generateSeed(b);
    return b;
}

SeedGenerator obtient le seedSource (chaîne) de SunEntries

String egdSource = SunEntries.getSeedSource();

SunEntries essaie d'obtenir la source à partir du système la propriété java.security.egd d'abord, si elle n'est pas trouvée, essaie d'obtenir la propriété securerandom.source à partir du fichier de propriétés java.security , si la propriété n'est pas trouvée renvoie une chaîne vide.

// name of the *System* property, takes precedence over PROP_RNDSOURCE
private final static String PROP_EGD = "java.security.egd";
// name of the *Security* property
private final static String PROP_RNDSOURCE = "securerandom.source";

final static String URL_DEV_RANDOM = "file:/dev/random";
final static String URL_DEV_URANDOM = "file:/dev/urandom";

private static final String seedSource;

static {
    seedSource = AccessController.doPrivileged(
            new PrivilegedAction<String>() {

        public String run() {
            String egdSource = System.getProperty(PROP_EGD, "");
            if (egdSource.length() != 0) {
                return egdSource;
            }
            egdSource = Security.getProperty(PROP_RNDSOURCE);
            if (egdSource == null) {
                return "";
            }
            return egdSource;
        }
    });
}

le SeedGenerator cochez cette valeur pour initialiser l'instance

// Static instance is created at link time
private static SeedGenerator instance;

private static final Debug debug = Debug.getInstance("provider");

final static String URL_DEV_RANDOM = SunEntries.URL_DEV_RANDOM;
final static String URL_DEV_URANDOM = SunEntries.URL_DEV_URANDOM;

// Static initializer to hook in selected or best performing generator
static {
    String egdSource = SunEntries.getSeedSource();

    // Try the URL specifying the source
    // e.g. file:/dev/random
    //
    // The URL file:/dev/random or file:/dev/urandom is used to indicate
    // the SeedGenerator using OS support, if available.
    // On Windows, the causes MS CryptoAPI to be used.
    // On Solaris and Linux, this is the identical to using
    // URLSeedGenerator to read from /dev/random

    if (egdSource.equals(URL_DEV_RANDOM) || egdSource.equals(URL_DEV_URANDOM)) {
        try {
            instance = new NativeSeedGenerator();
            if (debug != null) {
                debug.println("Using operating system seed generator");
            }
        } catch (IOException e) {
            if (debug != null) {
                debug.println("Failed to use operating system seed "
                              + "generator: " + e.toString());
            }
        }
    } else if (egdSource.length() != 0) {
        try {
            instance = new URLSeedGenerator(egdSource);
            if (debug != null) {
                debug.println("Using URL seed generator reading from "
                              + egdSource);
            }
        } catch (IOException e) {
            if (debug != null)
                debug.println("Failed to create seed generator with "
                              + egdSource + ": " + e.toString());
        }
    }

    // Fall back to ThreadedSeedGenerator
    if (instance == null) {
        if (debug != null) {
            debug.println("Using default threaded seed generator");
        }
        instance = new ThreadedSeedGenerator();
    }
}

si la source est

final static String URL_DEV_RANDOM = "file:/dev/random";

ou

final static String URL_DEV_URANDOM = "file:/dev/urandom"

utilise le NativeSeedGenerator , sur Windows essaie d'utiliser le natif CryptoAPI sur Linux la classe étend simplement le SeedGenerator.URLSeedGenerator

package sun.security.provider;

import java.io.IOException;

/**
 * Native seed generator for Unix systems. Inherit everything from
 * URLSeedGenerator.
 *
 */
class NativeSeedGenerator extends SeedGenerator.URLSeedGenerator {

    NativeSeedGenerator() throws IOException {
        super();
    }

}

et appel au constructeur de superclasses qui charge /dev/random par défaut

URLSeedGenerator() throws IOException {
    this(SeedGenerator.URL_DEV_RANDOM);
}

ainsi, OpenJDK utilise /dev/random par défaut jusqu'à ce que vous ne définissiez pas une autre valeur dans la propriété du système java.security.egd ou dans la propriété securerandom.source du fichier de propriétés de sécurité.

si vous voulez voir les résultats de lecture en utilisant strace vous pouvez changer la ligne de commande et ajouter l'expression trace=open,read 1519280920"

sudo strace -o a.strace -f -e trace=open,read java class

vous pouvez voir quelque chose comme ceci (j'ai fait le test avec Oracle JDK 6)

13225 open("/dev/random", O_RDONLY)     = 8
13225 read(8, "@", 1)                   = 1
13225 read(3, "PK\n"151990920""151990920""151990920""151990920""151990920"RyzB075u"151990920""151990920"u"151990920""151990920" "151990920""151990920""151990920"", 30) = 30
....
....

la section Wiki Tomcat pour un démarrage plus rapide suggère d'utiliser une source entropy non bloquante comme / dev / urandom si vous rencontrez des retards au démarrage

plus d'informations: https://wiki.apache.org/tomcat/HowTo/FasterStartUp#Entropy_Source

Espérons que cette aide.

3
répondu vzamanillo 2014-11-27 20:32:10

le problème n'est pas SecureRandom per se mais que /dev/random bloque si elle n'a pas assez de données. Vous pouvez utiliser urandom à la place mais cela pourrait ne pas être une bonne idée si vous avez besoin de graines cryptographiquement fortes aléatoires. Sur les systèmes Linux sans tête, vous pouvez installer le démon haveged. Cela garde /dev / random rempli de suffisamment de données pour que les appels n'aient pas à attendre que l'entropie nécessaire soit générée. J'ai fait cela sur une instance de Debian Aws et j'ai regardé SecureRandom generateBytes les appels passent de 25 secondes à sub milliseconde (Openjdk 1.7 something, can't remember specifically what version).

2
répondu thunder 2015-05-20 19:57:04