2014-11-27 21 views
5

Sto tentando di eseguire il debug di alcune risposte lente servite da un'app distribuita su Tomcat. In questo momento mi sto concentrando su SecureRandom e /dev/random (alcune delle altre cause probabili sono state esaminate e escluse). Lo schema è il seguente:Tempi di risposta al servizio lenti: Java SecureRandom e/dev/random

  • La prima chiamata prende esattamente 30.0 secondo xy dopo Tomcat riavvio (anche se la richiesta arriva 4 minuti dopo l'avvio)
  • Più tardi, alcune chiamate prendono esattamente 15,0 secondi PQ (non c'era nessun modello specifico che ho potuto stabilire, pq essendo il tempo orario preso in TP99)

la chiamata di servizio prevede la crittografia e la decodifica (AES/ECB/PKCS5Padding).

È possibile che l'avvio/ripopolamento di SecureRandom porti a questo?

(Anche se, c'è un registro scritto in catalina.log che dice "Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [28,760] milliseconds.")

Inoltre, al fine di verificare se /dev/random o /dev/urandom viene utilizzato, ho usato il test da this question. Con mia sorpresa, non ho visto le letture di nessuno di loro a differenza di come accade nella domanda collegata. Queste sono le ultime righe del registro strace:

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 

Che è poi stato utilizzato per la semina SecureRandom?

FYI, 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) 
+0

Si potrebbe provare una piccola applicazione che crea un nuovo gen casuale (usando 'nuovo SecureRandom') e quindi leggere alcuni byte da esso, vedere se legge da 'urandom'. Assicurati di indirizzare lo stesso runtime Java e controlla se la proprietà 'java.security.egd' non è impostata usando' java -D' per Tomcat. –

+0

Ho eseguito un programma di esempio (codice preso dalla domanda sul collegamento). Non ha letto né di/dev/random né di/dev/urandom come indicato nei registri strace allegati alla domanda. –

+1

Puoi anche controllare 'jre/lib/security/java.security' e controllare come viene definito' securerandom.source'? –

risposta

3

non ho potuto controllare la versione di cemento OpenJDK, ma ho potuto verificare jdk6-b33.

SecureRandom utilizza SeedGenerator per ottenere i byte seme

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

SeedGenerator ottiene il seedSource (String) da SunEntries

String egdSource = SunEntries.getSeedSource(); 

SunEntries tentativi per ottenere la fonte dalla proprietà di sistema java.security.egd primo luogo, se è non trovato quindi tenta di ottenere la proprietà securerandom.source dal file delle proprietà java.security, se la proprietà è non trovato restituisce una stringa vuota.

// 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; 
     } 
    }); 
} 

il controllo SeedGenerator questo valore per inizializzare l'istanza

// 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(); 
    } 
} 

se la sorgente è

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

o

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

utilizza il NativeSeedGenerator, sulla finestra s tenta di utilizzare il nativo CryptoAPI su Linux la classe estende semplicemente il 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(); 
    } 

} 

e chiamare il costruttore della superclasse che carica /dev/random di default

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

così, OpenJDK utilizza /dev/random di default fino a che fare non impostare un altro valore nella proprietà di sistema java.security.egd o nella proprietà securerandom.source del file delle proprietà di sicurezza.

Se si desidera vedere i risultati di lettura che utilizzano strace è possibile modificare la riga di comando e aggiungere l'espressione trace=open,read

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

la si può vedere qualcosa come questo (ho fatto la prova con Oracle JDK 6)

13225 open("/dev/random", O_RDONLY)  = 8 
13225 read(8, "@", 1)     = 1 
13225 read(3, "PK\3\4\n\0\0\0\0\0RyzB\36\320\267\325u\4\0\0u\4\0\0 \0\0\0", 30) = 30 
.... 
.... 

sezione Il Tomcat Wiki per l'avvio veloce suggerire utilizzando una fonte di entropia non bloccante come/dev/urandom se si verificano ritardi durante l'avvio

012.351.

Altre informazioni: https://wiki.apache.org/tomcat/HowTo/FasterStartUp#Entropy_Source

Spero che questo aiuti.

+1

Grazie per le tracce 'read', confermo che il' \ dev \ urandom' viene utilizzato in questa JVM. Non vi è alcun overdide e la fonte sicura è anche '\ dev \ urandom'.Quindi, questo esclude la possibilità di SecureRandom come causa e dovrò cercare altre possibili cause. –

2

Il problema non è SecureRandom di per se ma che/dev/random blocca se non ha dati sufficienti. Puoi usare urandom invece, ma potrebbe non essere una buona idea se hai bisogno di semi casuali crittograficamente forti. Su sistemi Linux headless è possibile installare il demone haveged. Ciò mantiene/dev/random aggiornato con dati sufficienti in modo che le chiamate non debbano attendere che venga generata l'entropia richiesta. L'ho fatto su un'istanza di Debian Aws e ho guardato SecureRandom generateBytes per le chiamate da 25 secondi a un millisecondo (Openjdk 1.7 qualcosa, non ricordo esattamente quale versione).