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)
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.
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).