# HG changeset patch # User wetmore # Date 1365739404 25200 # Node ID 0f93bd5cc8d7adc4384f503ec616275790bfeccf # Parent ea7976ed9bc675eeb2475892efd3bcf889ca25fd 6425477: Better support for generation of high entropy random numbers Reviewed-by: xuelei, weijun, mullan diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/classes/java/security/SecureRandom.java --- a/src/share/classes/java/security/SecureRandom.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/classes/java/security/SecureRandom.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.security; import java.util.*; +import java.util.regex.*; import java.security.Provider.Service; @@ -79,7 +80,7 @@ * * Note: Depending on the implementation, the generateSeed and * nextBytes methods may block as entropy is being gathered, - * for example, if they need to read from /dev/random on various unix-like + * for example, if they need to read from /dev/random on various Unix-like * operating systems. * * @see java.security.SecureRandomSpi @@ -428,6 +429,7 @@ * * @see #getSeed */ + @Override public void setSeed(long seed) { /* * Ignore call from super constructor (as well as any other calls @@ -450,7 +452,7 @@ * * @param bytes the array to be filled in with random bytes. */ - + @Override synchronized public void nextBytes(byte[] bytes) { secureRandomSpi.engineNextBytes(bytes); } @@ -469,14 +471,16 @@ * @return an int containing the user-specified number * of pseudo-random bits (right justified, with leading zeros). */ + @Override final protected int next(int numBits) { int numBytes = (numBits+7)/8; byte b[] = new byte[numBytes]; int next = 0; nextBytes(b); - for (int i = 0; i < numBytes; i++) + for (int i = 0; i < numBytes; i++) { next = (next << 8) + (b[i] & 0xFF); + } return next >>> (numBytes*8 - numBits); } @@ -499,8 +503,9 @@ * @see #setSeed */ public static byte[] getSeed(int numBytes) { - if (seedGenerator == null) + if (seedGenerator == null) { seedGenerator = new SecureRandom(); + } return seedGenerator.generateSeed(numBytes); } @@ -549,6 +554,104 @@ return null; } + /* + * Lazily initialize since Pattern.compile() is heavy. + * Effective Java (2nd Edition), Item 71. + */ + private static final class StrongPatternHolder { + /* + * Entries are alg:prov separated by , + * Allow for prepended/appended whitespace between entries. + * + * Capture groups: + * 1 - alg + * 2 - :prov (optional) + * 3 - prov (optional) + * 4 - ,nextEntry (optional) + * 5 - nextEntry (optional) + */ + private static Pattern pattern = + Pattern.compile( + "\\s*([\\S&&[^:,]]*)(\\:([\\S&&[^,]]*))?\\s*(\\,(.*))?"); + } + + /** + * Returns a {@code SecureRandom} object that was selected by using + * the algorithms/providers specified in the {@code + * securerandom.strongAlgorithms} Security property. + *

+ * Some situations require strong random values, such as when + * creating high-value/long-lived secrets like RSA public/private + * keys. To help guide applications in selecting a suitable strong + * {@code SecureRandom} implementation, Java distributions should + * include a list of known strong {@code SecureRandom} + * implementations in the {@code securerandom.strongAlgorithms} + * Security property. + * + *

+     *     SecureRandom sr = SecureRandom.getStrongSecureRandom();
+     *
+     *     if (sr == null) {
+     *         // Decide if this is a problem, and whether to recover.
+     *         sr = new SecureRandom();
+     *         if (!goodEnough(sr)) {
+     *             return;
+     *         }
+     *     }
+     *
+     *     keyPairGenerator.initialize(2048, sr);
+     * 
+ * + * @return a strong {@code SecureRandom} implementation as indicated + * by the {@code securerandom.strongAlgorithms} Security property, or + * null if none are available. + * + * @see Security#getProperty(String) + * + * @since 1.8 + */ + public static SecureRandom getStrongSecureRandom() { + + String property = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return Security.getProperty( + "securerandom.strongAlgorithms"); + } + }); + + if ((property == null) || (property.length() == 0)) { + return null; + } + + String remainder = property; + while (remainder != null) { + Matcher m; + if ((m = StrongPatternHolder.pattern.matcher( + remainder)).matches()) { + + String alg = m.group(1); + String prov = m.group(3); + + try { + if (prov == null) { + return SecureRandom.getInstance(alg); + } else { + return SecureRandom.getInstance(alg, prov); + } + } catch (NoSuchAlgorithmException | + NoSuchProviderException e) { + } + remainder = m.group(5); + } else { + remainder = null; + } + } + + return null; + } + // Declare serialVersionUID to be compatible with JDK1.1 static final long serialVersionUID = 4940670005562187L; diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/classes/sun/security/pkcs11/P11SecureRandom.java --- a/src/share/classes/sun/security/pkcs11/P11SecureRandom.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/classes/sun/security/pkcs11/P11SecureRandom.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,9 @@ package sun.security.pkcs11; -import java.util.*; import java.io.*; import java.security.*; - import sun.security.pkcs11.wrapper.*; -import sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * SecureRandom implementation class. Some tokens support only @@ -88,6 +85,7 @@ } // see JCA spec + @Override protected synchronized void engineSetSeed(byte[] seed) { if (seed == null) { throw new NullPointerException("seed must not be null"); @@ -119,6 +117,7 @@ } // see JCA spec + @Override protected void engineNextBytes(byte[] bytes) { if ((bytes == null) || (bytes.length == 0)) { return; @@ -149,6 +148,7 @@ } // see JCA spec + @Override protected byte[] engineGenerateSeed(int numBytes) { byte[] b = new byte[numBytes]; engineNextBytes(b); diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/classes/sun/security/provider/SecureRandom.java --- a/src/share/classes/sun/security/provider/SecureRandom.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/classes/sun/security/provider/SecureRandom.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,7 @@ } /** - * This constructor is used to instatiate the private seeder object + * This constructor is used to instantiate the private seeder object * with a given seed from the SeedGenerator. * * @param seed the seed. @@ -94,7 +94,7 @@ */ private void init(byte[] seed) { try { - digest = MessageDigest.getInstance ("SHA"); + digest = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException e) { throw new InternalError("internal error: SHA-1 not available.", e); } @@ -120,7 +120,10 @@ * * @return the seed bytes. */ + @Override public byte[] engineGenerateSeed(int numBytes) { + // Neither of the SeedGenerator implementations require + // locking, so no sync needed here. byte[] b = new byte[numBytes]; SeedGenerator.generateSeed(b); return b; @@ -133,19 +136,21 @@ * * @param seed the seed. */ + @Override synchronized public void engineSetSeed(byte[] seed) { if (state != null) { digest.update(state); - for (int i = 0; i < state.length; i++) + for (int i = 0; i < state.length; i++) { state[i] = 0; + } } state = digest.digest(seed); } private static void updateState(byte[] state, byte[] output) { int last = 1; - int v = 0; - byte t = 0; + int v; + byte t; boolean zf = false; // state(n + 1) = (state(n) + output(n) + 1) % 2^160; @@ -162,8 +167,9 @@ } // Make sure at least one bit changes! - if (!zf) + if (!zf) { state[0]++; + } } /** @@ -193,6 +199,7 @@ * * @param bytes the array to be filled in with random bytes. */ + @Override public synchronized void engineNextBytes(byte[] result) { int index = 0; int todo; @@ -258,7 +265,7 @@ s.defaultReadObject (); try { - digest = MessageDigest.getInstance ("SHA"); + digest = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException e) { throw new InternalError("internal error: SHA-1 not available.", e); } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/classes/sun/security/provider/SeedGenerator.java --- a/src/share/classes/sun/security/provider/SeedGenerator.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/classes/sun/security/provider/SeedGenerator.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,13 @@ package sun.security.provider; /** - *

This class generates seeds for the cryptographically strong random - * number generator. - *

The seed is produced using one of two techniques, via a computation + * This class generates seeds for the SHA1PRNG cryptographically strong + * random number generator. + *

+ * The seed is produced using one of two techniques, via a computation * of current system activity or from an entropy gathering device. - *

In the default technique the seed is produced by counting the + *

+ * In the default technique the seed is produced by counting the * number of times the VM manages to loop in a given period. This number * roughly reflects the machine load at that point in time. * The samples are translated using a permutation (s-box) @@ -41,23 +43,24 @@ * We also create a number of sleeper threads which add entropy * to the system by keeping the scheduler busy. * Twenty such samples should give us roughly 160 bits of randomness. - *

These values are gathered in the background by a daemon thread + *

+ * These values are gathered in the background by a daemon thread * thus allowing the system to continue performing it's different * activites, which in turn add entropy to the random seed. - *

The class also gathers miscellaneous system information, some + *

+ * The class also gathers miscellaneous system information, some * machine dependent, some not. This information is then hashed together * with the 20 seed bytes. - *

The alternative to the above approach is to acquire seed material + *

+ * The alternative to the above approach is to acquire seed material * from an entropy gathering device, such as /dev/random. This can be - * accomplished by setting the value of the "securerandom.source" - * security property (in the Java security properties file) to a URL - * specifying the location of the entropy gathering device. + * accomplished by setting the value of the {@code securerandom.source} + * Security property to a URL specifying the location of the entropy + * gathering device, or by setting the {@code java.security.egd} System + * property. + *

* In the event the specified URL cannot be accessed the default - * mechanism is used. - * The Java security properties file is located in the file named - * <JAVA_HOME>/lib/security/java.security. - * <JAVA_HOME> refers to the value of the java.home system property, - * and specifies the directory where the JRE is installed. + * threading mechanism is used. * * @author Joshua Bloch * @author Gadi Guy @@ -81,27 +84,28 @@ 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 the URL specifying the source (e.g. file:/dev/random) + * + * The URLs "file:/dev/random" or "file:/dev/urandom" are used to + * indicate the SeedGenerator should use OS support, if available. + * + * On Windows, this causes the MS CryptoAPI seeder to be used. + * + * On Solaris/Linux/MacOS, this is identical to using + * URLSeedGenerator to read from /dev/[u]random + */ + if (egdSource.equals(SunEntries.URL_DEV_RANDOM) || + egdSource.equals(SunEntries.URL_DEV_URANDOM)) { try { - instance = new NativeSeedGenerator(); + instance = new NativeSeedGenerator(egdSource); if (debug != null) { - debug.println("Using operating system seed generator"); + debug.println( + "Using operating system seed generator" + egdSource); } } catch (IOException e) { if (debug != null) { @@ -117,9 +121,10 @@ + egdSource); } } catch (IOException e) { - if (debug != null) + if (debug != null) { debug.println("Failed to create seed generator with " + egdSource + ": " + e.toString()); + } } } @@ -161,8 +166,8 @@ java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { + @Override public Void run() { - try { // System properties can change from machine to machine String s; @@ -180,7 +185,9 @@ // The temporary dir File f = new File(p.getProperty("java.io.tmpdir")); int count = 0; - try (DirectoryStream stream = Files.newDirectoryStream(f.toPath())) { + try ( + DirectoryStream stream = + Files.newDirectoryStream(f.toPath())) { // We use a Random object to choose what file names // should be used. Otherwise on a machine with too // many files, the same first 1024 files always get @@ -189,7 +196,8 @@ Random r = new Random(); for (Path entry: stream) { if (count < 512 || r.nextBoolean()) { - md.update(entry.getFileName().toString().getBytes()); + md.update(entry.getFileName() + .toString().getBytes()); } if (count++ > 1024) { break; @@ -236,7 +244,8 @@ */ - private static class ThreadedSeedGenerator extends SeedGenerator implements Runnable { + private static class ThreadedSeedGenerator extends SeedGenerator + implements Runnable { // Queue is used to collect seed bytes private byte[] pool; private int start, end, count; @@ -245,11 +254,10 @@ ThreadGroup seedGroup; /** - * The constructor is only called once to construct the one - * instance we actually use. It instantiates the message digest - * and starts the thread going. - */ - + * The constructor is only called once to construct the one + * instance we actually use. It instantiates the message digest + * and starts the thread going. + */ ThreadedSeedGenerator() { pool = new byte[20]; start = end = 0; @@ -266,16 +274,18 @@ final ThreadGroup[] finalsg = new ThreadGroup[1]; Thread t = java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { + @Override public Thread run() { ThreadGroup parent, group = Thread.currentThread().getThreadGroup(); - while ((parent = group.getParent()) != null) + while ((parent = group.getParent()) != null) { group = parent; + } finalsg[0] = new ThreadGroup (group, "SeedGenerator ThreadGroup"); Thread newT = new Thread(finalsg[0], - ThreadedSeedGenerator.this, - "SeedGenerator Thread"); + ThreadedSeedGenerator.this, + "SeedGenerator Thread"); newT.setPriority(Thread.MIN_PRIORITY); newT.setDaemon(true); return newT; @@ -289,21 +299,23 @@ * This method does the actual work. It collects random bytes and * pushes them into the queue. */ + @Override final public void run() { try { while (true) { // Queue full? Wait till there's room. synchronized(this) { - while (count >= pool.length) + while (count >= pool.length) { wait(); + } } int counter, quanta; byte v = 0; // Spin count must not be under 64000 - for (counter = quanta = 0; (counter < 64000) && (quanta < 6); - quanta++) { + for (counter = quanta = 0; + (counter < 64000) && (quanta < 6); quanta++) { // Start some noisy threads try { @@ -313,14 +325,12 @@ t.start(); } catch (Exception e) { throw new InternalError("internal error: " + - "SeedGenerator thread creation error." - , e); + "SeedGenerator thread creation error.", e); } // We wait 250milli quanta, so the minimum wait time // cannot be under 250milli. int latch = 0; - latch = 0; long l = System.currentTimeMillis() + 250; while (System.currentTimeMillis() < l) { synchronized(this){}; @@ -339,16 +349,16 @@ pool[end] = v; end++; count++; - if (end >= pool.length) + if (end >= pool.length) { end = 0; + } notifyAll(); } } } catch (Exception e) { throw new InternalError("internal error: " + - "SeedGenerator thread generated an exception." - , e); + "SeedGenerator thread generated an exception.", e); } } @@ -360,19 +370,20 @@ } byte getSeedByte() { - byte b = 0; + byte b; try { // Wait for it... synchronized(this) { - while (count <= 0) + while (count <= 0) { wait(); + } } } catch (Exception e) { - if (count <= 0) + if (count <= 0) { throw new InternalError("internal error: " + - "SeedGenerator thread generated an exception." - ,e); + "SeedGenerator thread generated an exception.", e); + } } synchronized(this) { @@ -381,8 +392,9 @@ pool[start] = 0; start++; count--; - if (start == pool.length) + if (start == pool.length) { start = 0; + } // Notify the daemon thread, just in case it is // waiting for us to make room in the queue. @@ -430,12 +442,13 @@ * thus adding entropy to the system load. * At least one instance of this class is generated for every seed byte. */ - private static class BogusThread implements Runnable { + @Override final public void run() { try { - for(int i = 0; i < 5; i++) + for (int i = 0; i < 5; i++) { Thread.sleep(50); + } // System.gc(); } catch (Exception e) { } @@ -446,7 +459,7 @@ static class URLSeedGenerator extends SeedGenerator { private String deviceName; - private InputStream devRandom; + private InputStream seedStream; /** * The constructor is only called once to construct the one @@ -462,15 +475,12 @@ init(); } - URLSeedGenerator() throws IOException { - this(SeedGenerator.URL_DEV_RANDOM); - } - private void init() throws IOException { final URL device = new URL(deviceName); try { - devRandom = java.security.AccessController.doPrivileged + seedStream = java.security.AccessController.doPrivileged (new java.security.PrivilegedExceptionAction() { + @Override public InputStream run() throws IOException { /* * return a FileInputStream for file URLs and @@ -481,7 +491,8 @@ * can be slow to replenish. */ if (device.getProtocol().equalsIgnoreCase("file")) { - File deviceFile = getDeviceFile(device); + File deviceFile = + SunEntries.getDeviceFile(device); return new FileInputStream(deviceFile); } else { return device.openStream(); @@ -489,36 +500,8 @@ } }); } catch (Exception e) { - throw new IOException("Failed to open " + deviceName, e.getCause()); - } - } - - /* - * Use a URI to access this File. Previous code used a URL - * which is less strict on syntax. If we encounter a - * URISyntaxException we make best efforts for backwards - * compatibility. e.g. space character in deviceName string. - * - * Method called within PrivilegedExceptionAction block. - */ - private File getDeviceFile(URL device) throws IOException { - try { - URI deviceURI = device.toURI(); - if(deviceURI.isOpaque()) { - // File constructor does not accept opaque URI - URI localDir = new File(System.getProperty("user.dir")).toURI(); - String uriPath = localDir.toString() + - deviceURI.toString().substring(5); - return new File(URI.create(uriPath)); - } else { - return new File(deviceURI); - } - } catch (URISyntaxException use) { - /* - * Make best effort to access this File. - * We can try using the URL path. - */ - return new File(device.getPath()); + throw new IOException( + "Failed to open " + deviceName, e.getCause()); } } @@ -528,19 +511,19 @@ int read = 0; try { while (read < len) { - int count = devRandom.read(result, read, len - read); + int count = seedStream.read(result, read, len - read); // /dev/random blocks - should never have EOF - if (count < 0) - throw new InternalError("URLSeedGenerator " + deviceName + - " reached end of file"); + if (count < 0) { + throw new InternalError( + "URLSeedGenerator " + deviceName + + " reached end of file"); + } read += count; } } catch (IOException ioe) { throw new InternalError("URLSeedGenerator " + deviceName + - " generated exception: " + - ioe.getMessage(), ioe); + " generated exception: " + ioe.getMessage(), ioe); } } - } } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/classes/sun/security/provider/SunEntries.java --- a/src/share/classes/sun/security/provider/SunEntries.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/classes/sun/security/provider/SunEntries.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package sun.security.provider; +import java.io.*; +import java.net.*; import java.util.Map; import java.security.*; @@ -92,26 +94,41 @@ // if user selected /dev/urandom, we put it before SHA1PRNG, // otherwise after it boolean nativeAvailable = NativePRNG.isAvailable(); - boolean useUrandom = seedSource.equals(URL_DEV_URANDOM); - if (nativeAvailable && useUrandom) { + boolean useNativePRNG = seedSource.equals(URL_DEV_URANDOM) || + seedSource.equals(URL_DEV_RANDOM); + + if (nativeAvailable && useNativePRNG) { map.put("SecureRandom.NativePRNG", "sun.security.provider.NativePRNG"); } map.put("SecureRandom.SHA1PRNG", "sun.security.provider.SecureRandom"); - if (nativeAvailable && !useUrandom) { + if (nativeAvailable && !useNativePRNG) { map.put("SecureRandom.NativePRNG", "sun.security.provider.NativePRNG"); } + if (NativePRNG.Blocking.isAvailable()) { + map.put("SecureRandom.NativePRNGBlocking", + "sun.security.provider.NativePRNG$Blocking"); + } + + if (NativePRNG.NonBlocking.isAvailable()) { + map.put("SecureRandom.NativePRNGNonBlocking", + "sun.security.provider.NativePRNG$NonBlocking"); + } + /* * Signature engines */ - map.put("Signature.SHA1withDSA", "sun.security.provider.DSA$SHA1withDSA"); + map.put("Signature.SHA1withDSA", + "sun.security.provider.DSA$SHA1withDSA"); map.put("Signature.NONEwithDSA", "sun.security.provider.DSA$RawDSA"); map.put("Alg.Alias.Signature.RawDSA", "NONEwithDSA"); - map.put("Signature.SHA224withDSA", "sun.security.provider.DSA$SHA224withDSA"); - map.put("Signature.SHA256withDSA", "sun.security.provider.DSA$SHA256withDSA"); + map.put("Signature.SHA224withDSA", + "sun.security.provider.DSA$SHA224withDSA"); + map.put("Signature.SHA256withDSA", + "sun.security.provider.DSA$SHA256withDSA"); String dsaKeyClasses = "java.security.interfaces.DSAPublicKey" + "|java.security.interfaces.DSAPrivateKey"; @@ -128,13 +145,15 @@ map.put("Alg.Alias.Signature.SHAwithDSA", "SHA1withDSA"); map.put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); map.put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", - "SHA1withDSA"); + "SHA1withDSA"); map.put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); map.put("Alg.Alias.Signature.1.3.14.3.2.13", "SHA1withDSA"); map.put("Alg.Alias.Signature.1.3.14.3.2.27", "SHA1withDSA"); - map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); + map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.1", + "SHA224withDSA"); map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); - map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); + map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.2", + "SHA256withDSA"); map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); /* @@ -160,17 +179,21 @@ map.put("MessageDigest.SHA-224", "sun.security.provider.SHA2$SHA224"); map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.4", "SHA-224"); - map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.4", "SHA-224"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.4", + "SHA-224"); map.put("MessageDigest.SHA-256", "sun.security.provider.SHA2$SHA256"); map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.1", "SHA-256"); - map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.1", "SHA-256"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.1", + "SHA-256"); map.put("MessageDigest.SHA-384", "sun.security.provider.SHA5$SHA384"); map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.2", "SHA-384"); - map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.2", "SHA-384"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.2", + "SHA-384"); map.put("MessageDigest.SHA-512", "sun.security.provider.SHA5$SHA512"); map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512"); - map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", "SHA-512"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", + "SHA-512"); /* * Algorithm Parameter Generator engines @@ -296,6 +319,7 @@ seedSource = AccessController.doPrivileged( new PrivilegedAction() { + @Override public String run() { String egdSource = System.getProperty(PROP_EGD, ""); if (egdSource.length() != 0) { @@ -314,4 +338,36 @@ return seedSource; } + /* + * Use a URI to access this File. Previous code used a URL + * which is less strict on syntax. If we encounter a + * URISyntaxException we make best efforts for backwards + * compatibility. e.g. space character in deviceName string. + * + * Method called within PrivilegedExceptionAction block. + * + * Moved from SeedGenerator to avoid initialization problems with + * signed providers. + */ + static File getDeviceFile(URL device) throws IOException { + try { + URI deviceURI = device.toURI(); + if(deviceURI.isOpaque()) { + // File constructor does not accept opaque URI + URI localDir = new File( + System.getProperty("user.dir")).toURI(); + String uriPath = localDir.toString() + + deviceURI.toString().substring(5); + return new File(URI.create(uriPath)); + } else { + return new File(deviceURI); + } + } catch (URISyntaxException use) { + /* + * Make best effort to access this File. + * We can try using the URL path. + */ + return new File(device.getPath()); + } + } } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/lib/security/java.security-linux --- a/src/share/lib/security/java.security-linux Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/lib/security/java.security-linux Thu Apr 11 21:03:24 2013 -0700 @@ -76,26 +76,57 @@ security.provider.9=sun.security.smartcardio.SunPCSC # -# Select the source of seed data for SecureRandom. By default an -# attempt is made to use the entropy gathering device specified by -# the securerandom.source property. If an exception occurs when -# accessing the URL then the traditional system/thread activity -# algorithm is used. +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. # -# On Solaris and Linux systems, if file:/dev/urandom is specified and it -# exists, a special SecureRandom implementation is activated by default. -# This "NativePRNG" reads random bytes directly from /dev/urandom. +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. # -# On Windows systems, the URLs file:/dev/random and file:/dev/urandom -# enables use of the Microsoft CryptoAPI seed functionality. +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass # -securerandom.source=file:/dev/urandom +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. # -# The entropy gathering device is described as a URL and can also -# be specified with the system property "java.security.egd". For example, -# -Djava.security.egd=file:/dev/urandom -# Specifying this system property will override the securerandom.source -# setting. +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN # # Class to instantiate as the javax.security.auth.login.Configuration @@ -159,9 +190,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # List of comma-separated packages that start with or equal this string @@ -187,9 +218,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # Determines whether this properties file can be appended to diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/lib/security/java.security-macosx --- a/src/share/lib/security/java.security-macosx Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/lib/security/java.security-macosx Thu Apr 11 21:03:24 2013 -0700 @@ -77,26 +77,57 @@ security.provider.10=apple.security.AppleProvider # -# Select the source of seed data for SecureRandom. By default an -# attempt is made to use the entropy gathering device specified by -# the securerandom.source property. If an exception occurs when -# accessing the URL then the traditional system/thread activity -# algorithm is used. +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. # -# On Solaris and Linux systems, if file:/dev/urandom is specified and it -# exists, a special SecureRandom implementation is activated by default. -# This "NativePRNG" reads random bytes directly from /dev/urandom. +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. # -# On Windows systems, the URLs file:/dev/random and file:/dev/urandom -# enables use of the Microsoft CryptoAPI seed functionality. +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass # -securerandom.source=file:/dev/urandom +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. # -# The entropy gathering device is described as a URL and can also -# be specified with the system property "java.security.egd". For example, -# -Djava.security.egd=file:/dev/urandom -# Specifying this system property will override the securerandom.source -# setting. +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN # # Class to instantiate as the javax.security.auth.login.Configuration @@ -160,9 +191,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ apple. # @@ -189,9 +220,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ apple. # diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/lib/security/java.security-solaris --- a/src/share/lib/security/java.security-solaris Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/lib/security/java.security-solaris Thu Apr 11 21:03:24 2013 -0700 @@ -78,26 +78,57 @@ security.provider.11=sun.security.smartcardio.SunPCSC # -# Select the source of seed data for SecureRandom. By default an -# attempt is made to use the entropy gathering device specified by -# the securerandom.source property. If an exception occurs when -# accessing the URL then the traditional system/thread activity -# algorithm is used. +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. # -# On Solaris and Linux systems, if file:/dev/urandom is specified and it -# exists, a special SecureRandom implementation is activated by default. -# This "NativePRNG" reads random bytes directly from /dev/urandom. +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. # -# On Windows systems, the URLs file:/dev/random and file:/dev/urandom -# enables use of the Microsoft CryptoAPI seed functionality. +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass # -securerandom.source=file:/dev/urandom +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. # -# The entropy gathering device is described as a URL and can also -# be specified with the system property "java.security.egd". For example, -# -Djava.security.egd=file:/dev/urandom -# Specifying this system property will override the securerandom.source -# setting. +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN # # Class to instantiate as the javax.security.auth.login.Configuration @@ -161,9 +192,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # List of comma-separated packages that start with or equal this string @@ -189,9 +220,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # Determines whether this properties file can be appended to @@ -429,4 +460,4 @@ # # Example: # jdk.tls.disabledAlgorithms=MD5, SHA1, DSA, RSA keySize < 2048 -i + diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/share/lib/security/java.security-windows --- a/src/share/lib/security/java.security-windows Thu Apr 11 19:36:52 2013 -0700 +++ b/src/share/lib/security/java.security-windows Thu Apr 11 21:03:24 2013 -0700 @@ -77,26 +77,57 @@ security.provider.10=sun.security.mscapi.SunMSCAPI # -# Select the source of seed data for SecureRandom. By default an -# attempt is made to use the entropy gathering device specified by -# the securerandom.source property. If an exception occurs when -# accessing the URL then the traditional system/thread activity -# algorithm is used. +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. # -# On Solaris and Linux systems, if file:/dev/urandom is specified and it -# exists, a special SecureRandom implementation is activated by default. -# This "NativePRNG" reads random bytes directly from /dev/urandom. +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. # -# On Windows systems, the URLs file:/dev/random and file:/dev/urandom -# enables use of the Microsoft CryptoAPI seed functionality. +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass # -securerandom.source=file:/dev/urandom +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. # -# The entropy gathering device is described as a URL and can also -# be specified with the system property "java.security.egd". For example, -# -Djava.security.egd=file:/dev/urandom -# Specifying this system property will override the securerandom.source -# setting. +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=Windows-PRNG:SunMSCAPI # # Class to instantiate as the javax.security.auth.login.Configuration @@ -160,9 +191,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # List of comma-separated packages that start with or equal this string @@ -188,9 +219,9 @@ com.sun.org.glassfish.gmbal.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ - jdk.internal.,\ - jdk.nashorn.internal.,\ - jdk.nashorn.tools. + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools. # # Determines whether this properties file can be appended to diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/solaris/classes/sun/security/provider/NativePRNG.java --- a/src/solaris/classes/sun/security/provider/NativePRNG.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/solaris/classes/sun/security/provider/NativePRNG.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,34 +26,47 @@ package sun.security.provider; import java.io.*; - +import java.net.*; import java.security.*; -import java.security.SecureRandom; +import sun.security.util.Debug; /** - * Native PRNG implementation for Solaris/Linux. It interacts with - * /dev/random and /dev/urandom, so it is only available if those - * files are present. Otherwise, SHA1PRNG is used instead of this class. - * - * getSeed() and setSeed() directly read/write /dev/random. However, - * /dev/random is only writable by root in many configurations. Because - * we cannot just ignore bytes specified via setSeed(), we keep a - * SHA1PRNG around in parallel. - * - * nextBytes() reads the bytes directly from /dev/urandom (and then - * mixes them with bytes from the SHA1PRNG for the reasons explained - * above). Reading bytes from /dev/urandom means that constantly get - * new entropy the operating system has collected. This is a notable - * advantage over the SHA1PRNG model, which acquires entropy only - * initially during startup although the VM may be running for months. - * - * Also note that we do not need any initial pure random seed from - * /dev/random. This is an advantage because on some versions of Linux - * it can be exhausted very quickly and could thus impact startup time. - * + * Native PRNG implementation for Solaris/Linux/MacOS. + *

+ * It obtains seed and random numbers by reading system files such as + * the special device files /dev/random and /dev/urandom. This + * implementation respects the {@code securerandom.source} Security + * property and {@code java.security.egd} System property for obtaining + * seed material. If the file specified by the properties does not + * exist, /dev/random is the default seed source. /dev/urandom is + * the default source of random numbers. + *

+ * On some Unix platforms, /dev/random may block until enough entropy is + * available, but that may negatively impact the perceived startup + * time. By selecting these sources, this implementation tries to + * strike a balance between performance and security. + *

+ * generateSeed() and setSeed() attempt to directly read/write to the seed + * source. However, this file may only be writable by root in many + * configurations. Because we cannot just ignore bytes specified via + * setSeed(), we keep a SHA1PRNG around in parallel. + *

+ * nextBytes() reads the bytes directly from the source of random + * numbers (and then mixes them with bytes from the SHA1PRNG for the + * reasons explained above). Reading bytes from the random generator means + * that we are generally getting entropy from the operating system. This + * is a notable advantage over the SHA1PRNG model, which acquires + * entropy only initially during startup although the VM may be running + * for months. + *

+ * Also note for nextBytes() that we do not need any initial pure random + * seed from /dev/random. This is an advantage because on some versions + * of Linux entropy can be exhausted very quickly and could thus impact + * startup time. + *

* Finally, note that we use a singleton for the actual work (RandomIO) * to avoid having to open and close /dev/[u]random constantly. However, - * there may me many NativePRNG instances created by the JCA framework. + * there may be many NativePRNG instances created by the JCA framework. * * @since 1.5 * @author Andreas Sterbenz @@ -62,32 +75,121 @@ private static final long serialVersionUID = -6599091113397072932L; + private static final Debug debug = Debug.getInstance("provider"); + // name of the pure random file (also used for setSeed()) private static final String NAME_RANDOM = "/dev/random"; // name of the pseudo random file private static final String NAME_URANDOM = "/dev/urandom"; + // which kind of RandomIO object are we creating? + private enum Variant { + MIXED, BLOCKING, NONBLOCKING + } + // singleton instance or null if not available - private static final RandomIO INSTANCE = initIO(); + private static final RandomIO INSTANCE = initIO(Variant.MIXED); - private static RandomIO initIO() { - return AccessController.doPrivileged( - new PrivilegedAction() { - public RandomIO run() { - File randomFile = new File(NAME_RANDOM); - if (randomFile.exists() == false) { + /** + * Get the System egd source (if defined). We only allow "file:" + * URLs for now. If there is a egd value, parse it. + * + * @return the URL or null if not available. + */ + private static URL getEgdUrl() { + // This will return "" if nothing was set. + String egdSource = SunEntries.getSeedSource(); + URL egdUrl; + + if (egdSource.length() != 0) { + if (debug != null) { + debug.println("NativePRNG egdUrl: " + egdSource); + } + try { + egdUrl = new URL(egdSource); + if (!egdUrl.getProtocol().equalsIgnoreCase("file")) { return null; } - File urandomFile = new File(NAME_URANDOM); - if (urandomFile.exists() == false) { - return null; + } catch (MalformedURLException e) { + return null; + } + } else { + egdUrl = null; + } + + return egdUrl; + } + + /** + * Create a RandomIO object for all I/O of this Variant type. + */ + private static RandomIO initIO(final Variant v) { + return AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public RandomIO run() { + + File seedFile; + File nextFile; + + switch(v) { + case MIXED: + URL egdUrl; + File egdFile = null; + + if ((egdUrl = getEgdUrl()) != null) { + try { + egdFile = SunEntries.getDeviceFile(egdUrl); + } catch (IOException e) { + // Swallow, seedFile is still null + } + } + + // Try egd first. + if ((egdFile != null) && egdFile.canRead()) { + seedFile = egdFile; + } else { + // fall back to /dev/random. + seedFile = new File(NAME_RANDOM); + } + nextFile = new File(NAME_URANDOM); + break; + + case BLOCKING: + seedFile = new File(NAME_RANDOM); + nextFile = new File(NAME_RANDOM); + break; + + case NONBLOCKING: + seedFile = new File(NAME_URANDOM); + nextFile = new File(NAME_URANDOM); + break; + + default: + // Shouldn't happen! + return null; + } + + if (debug != null) { + debug.println("NativePRNG." + v + + " seedFile: " + seedFile + + " nextFile: " + nextFile); + } + + if (!seedFile.canRead() || !nextFile.canRead()) { + if (debug != null) { + debug.println("NativePRNG." + v + + " Couldn't read Files."); + } + return null; + } + + try { + return new RandomIO(seedFile, nextFile); + } catch (Exception e) { + return null; + } } - try { - return new RandomIO(randomFile, urandomFile); - } catch (Exception e) { - return null; - } - } }); } @@ -105,67 +207,173 @@ } // set the seed + @Override protected void engineSetSeed(byte[] seed) { INSTANCE.implSetSeed(seed); } // get pseudo random bytes + @Override protected void engineNextBytes(byte[] bytes) { INSTANCE.implNextBytes(bytes); } // get true random bytes + @Override protected byte[] engineGenerateSeed(int numBytes) { return INSTANCE.implGenerateSeed(numBytes); } /** + * A NativePRNG-like class that uses /dev/random for both + * seed and random material. + * + * Note that it does not respect the egd properties, since we have + * no way of knowing what those qualities are. + * + * This is very similar to the outer NativePRNG class, minimizing any + * breakage to the serialization of the existing implementation. + * + * @since 1.8 + */ + public static final class Blocking extends SecureRandomSpi { + private static final long serialVersionUID = -6396183145759983347L; + + private static final RandomIO INSTANCE = initIO(Variant.BLOCKING); + + // return whether this is available + static boolean isAvailable() { + return INSTANCE != null; + } + + // constructor, called by the JCA framework + public Blocking() { + super(); + if (INSTANCE == null) { + throw new AssertionError("NativePRNG$Blocking not available"); + } + } + + // set the seed + @Override + protected void engineSetSeed(byte[] seed) { + INSTANCE.implSetSeed(seed); + } + + // get pseudo random bytes + @Override + protected void engineNextBytes(byte[] bytes) { + INSTANCE.implNextBytes(bytes); + } + + // get true random bytes + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return INSTANCE.implGenerateSeed(numBytes); + } + } + + /** + * A NativePRNG-like class that uses /dev/urandom for both + * seed and random material. + * + * Note that it does not respect the egd properties, since we have + * no way of knowing what those qualities are. + * + * This is very similar to the outer NativePRNG class, minimizing any + * breakage to the serialization of the existing implementation. + * + * @since 1.8 + */ + public static final class NonBlocking extends SecureRandomSpi { + private static final long serialVersionUID = -1102062982994105487L; + + private static final RandomIO INSTANCE = initIO(Variant.NONBLOCKING); + + // return whether this is available + static boolean isAvailable() { + return INSTANCE != null; + } + + // constructor, called by the JCA framework + public NonBlocking() { + super(); + if (INSTANCE == null) { + throw new AssertionError( + "NativePRNG$NonBlocking not available"); + } + } + + // set the seed + @Override + protected void engineSetSeed(byte[] seed) { + INSTANCE.implSetSeed(seed); + } + + // get pseudo random bytes + @Override + protected void engineNextBytes(byte[] bytes) { + INSTANCE.implNextBytes(bytes); + } + + // get true random bytes + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return INSTANCE.implGenerateSeed(numBytes); + } + } + + /** * Nested class doing the actual work. Singleton, see INSTANCE above. */ private static class RandomIO { - // we buffer data we read from /dev/urandom for efficiency, + // we buffer data we read from the "next" file for efficiency, // but we limit the lifetime to avoid using stale bits // lifetime in ms, currently 100 ms (0.1 s) private final static long MAX_BUFFER_TIME = 100; - // size of the /dev/urandom buffer + // size of the "next" buffer private final static int BUFFER_SIZE = 32; - // In/OutputStream for /dev/random and /dev/urandom - private final InputStream randomIn, urandomIn; - private OutputStream randomOut; + // Holder for the seedFile. Used if we ever add seed material. + File seedFile; - // flag indicating if we have tried to open randomOut yet - private boolean randomOutInitialized; + // In/OutputStream for "seed" and "next" + private final InputStream seedIn, nextIn; + private OutputStream seedOut; + + // flag indicating if we have tried to open seedOut yet + private boolean seedOutInitialized; // SHA1PRNG instance for mixing // initialized lazily on demand to avoid problems during startup private volatile sun.security.provider.SecureRandom mixRandom; - // buffer for /dev/urandom bits - private final byte[] urandomBuffer; + // buffer for next bits + private final byte[] nextBuffer; - // number of bytes left in urandomBuffer + // number of bytes left in nextBuffer private int buffered; - // time we read the data into the urandomBuffer + // time we read the data into the nextBuffer private long lastRead; // mutex lock for nextBytes() private final Object LOCK_GET_BYTES = new Object(); - // mutex lock for getSeed() + // mutex lock for generateSeed() private final Object LOCK_GET_SEED = new Object(); // mutex lock for setSeed() private final Object LOCK_SET_SEED = new Object(); // constructor, called only once from initIO() - private RandomIO(File randomFile, File urandomFile) throws IOException { - randomIn = new FileInputStream(randomFile); - urandomIn = new FileInputStream(urandomFile); - urandomBuffer = new byte[BUFFER_SIZE]; + private RandomIO(File seedFile, File nextFile) throws IOException { + this.seedFile = seedFile; + seedIn = new FileInputStream(seedFile); + nextIn = new FileInputStream(nextFile); + nextBuffer = new byte[BUFFER_SIZE]; } // get the SHA1PRNG for mixing @@ -179,7 +387,7 @@ r = new sun.security.provider.SecureRandom(); try { byte[] b = new byte[20]; - readFully(urandomIn, b); + readFully(nextIn, b); r.engineSetSeed(b); } catch (IOException e) { throw new ProviderException("init failed", e); @@ -192,7 +400,7 @@ } // read data.length bytes from in - // /dev/[u]random are not normal files, so we need to loop the read. + // These are not normal files, so we need to loop the read. // just keep trying as long as we are making progress private static void readFully(InputStream in, byte[] data) throws IOException { @@ -201,22 +409,22 @@ while (len > 0) { int k = in.read(data, ofs, len); if (k <= 0) { - throw new EOFException("/dev/[u]random closed?"); + throw new EOFException("File(s) closed?"); } ofs += k; len -= k; } if (len > 0) { - throw new IOException("Could not read from /dev/[u]random"); + throw new IOException("Could not read from file(s)"); } } - // get true random bytes, just read from /dev/random + // get true random bytes, just read from "seed" private byte[] implGenerateSeed(int numBytes) { synchronized (LOCK_GET_SEED) { try { byte[] b = new byte[numBytes]; - readFully(randomIn, b); + readFully(seedIn, b); return b; } catch (IOException e) { throw new ProviderException("generateSeed() failed", e); @@ -225,26 +433,27 @@ } // supply random bytes to the OS - // write to /dev/random if possible + // write to "seed" if possible // always add the seed to our mixing random private void implSetSeed(byte[] seed) { synchronized (LOCK_SET_SEED) { - if (randomOutInitialized == false) { - randomOutInitialized = true; - randomOut = AccessController.doPrivileged( + if (seedOutInitialized == false) { + seedOutInitialized = true; + seedOut = AccessController.doPrivileged( new PrivilegedAction() { + @Override public OutputStream run() { try { - return new FileOutputStream(NAME_RANDOM, true); + return new FileOutputStream(seedFile, true); } catch (Exception e) { return null; } } }); } - if (randomOut != null) { + if (seedOut != null) { try { - randomOut.write(seed); + seedOut.write(seed); } catch (IOException e) { throw new ProviderException("setSeed() failed", e); } @@ -261,12 +470,12 @@ return; } lastRead = time; - readFully(urandomIn, urandomBuffer); - buffered = urandomBuffer.length; + readFully(nextIn, nextBuffer); + buffered = nextBuffer.length; } // get pseudo random bytes - // read from /dev/urandom and XOR with bytes generated by the + // read from "next" and XOR with bytes generated by the // mixing SHA1PRNG private void implNextBytes(byte[] data) { synchronized (LOCK_GET_BYTES) { @@ -276,9 +485,9 @@ int ofs = 0; while (len > 0) { ensureBufferValid(); - int bufferOfs = urandomBuffer.length - buffered; + int bufferOfs = nextBuffer.length - buffered; while ((len > 0) && (buffered > 0)) { - data[ofs++] ^= urandomBuffer[bufferOfs++]; + data[ofs++] ^= nextBuffer[bufferOfs++]; len--; buffered--; } @@ -288,7 +497,5 @@ } } } - } - } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/solaris/classes/sun/security/provider/NativeSeedGenerator.java --- a/src/solaris/classes/sun/security/provider/NativeSeedGenerator.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/solaris/classes/sun/security/provider/NativeSeedGenerator.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,8 @@ */ class NativeSeedGenerator extends SeedGenerator.URLSeedGenerator { - NativeSeedGenerator() throws IOException { - super(); + NativeSeedGenerator(String seedFile) throws IOException { + super(seedFile); } } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/windows/classes/sun/security/mscapi/PRNG.java --- a/src/windows/classes/sun/security/mscapi/PRNG.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/windows/classes/sun/security/mscapi/PRNG.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,7 @@ * * @param seed the seed. */ + @Override protected void engineSetSeed(byte[] seed) { if (seed != null) { generateSeed(-1, seed); @@ -69,6 +70,7 @@ * * @param bytes the array to be filled in with random bytes. */ + @Override protected void engineNextBytes(byte[] bytes) { if (bytes != null) { if (generateSeed(0, bytes) == null) { @@ -85,6 +87,7 @@ * * @return the seed bytes. */ + @Override protected byte[] engineGenerateSeed(int numBytes) { byte[] seed = generateSeed(numBytes, null); diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/windows/classes/sun/security/provider/NativePRNG.java --- a/src/windows/classes/sun/security/provider/NativePRNG.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/windows/classes/sun/security/provider/NativePRNG.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,4 +39,15 @@ return false; } + public static final class NonBlocking { + static boolean isAvailable() { + return false; + } + } + + public static final class Blocking { + static boolean isAvailable() { + return false; + } + } } diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 src/windows/classes/sun/security/provider/NativeSeedGenerator.java --- a/src/windows/classes/sun/security/provider/NativeSeedGenerator.java Thu Apr 11 19:36:52 2013 -0700 +++ b/src/windows/classes/sun/security/provider/NativeSeedGenerator.java Thu Apr 11 21:03:24 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,8 @@ * @exception IOException if CryptoAPI seeds are not available * on this platform. */ - NativeSeedGenerator() throws IOException { + NativeSeedGenerator(String seedFile) throws IOException { + // seedFile is ignored. super(); // try generating two random bytes to see if CAPI is available if (!nativeGenerateSeed(new byte[2])) { diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 test/sun/security/provider/SecureRandom/StrongSecureRandom.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sun/security/provider/SecureRandom/StrongSecureRandom.java Thu Apr 11 21:03:24 2013 -0700 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6425477 + * @summary Better support for generation of high entropy random numbers + * @run main/othervm StrongSecureRandom + */ +import java.security.*; +import java.util.*; + +/** + * This test assumes that the standard Sun providers are installed. + */ +public class StrongSecureRandom { + + private static String os = System.getProperty("os.name", "unknown"); + + private static void testDefaultEgd() throws Exception { + // No SecurityManager installed. + String s = Security.getProperty("securerandom.source"); + + System.out.println("Testing: default EGD: " + s); + if (!s.equals("file:/dev/random")) { + throw new Exception("Default is not 'file:/dev/random'"); + } + } + + private static void testSHA1PRNGImpl() throws Exception { + SecureRandom sr; + byte[] ba; + + String urandom = "file:/dev/urandom"; + + System.out.println("Testing new SeedGenerator and EGD"); + + Security.setProperty("securerandom.source", urandom); + if (!Security.getProperty("securerandom.source").equals(urandom)) { + throw new Exception("Couldn't set securerandom.source"); + } + + /* + * Take out a large number of bytes in hopes of blocking. + * Don't expect this to happen, unless something is broken on Linux + */ + sr = SecureRandom.getInstance("SHA1PRNG"); + if (!sr.getAlgorithm().equals("SHA1PRNG")) { + throw new Exception("sr.getAlgorithm(): " + sr.getAlgorithm()); + } + + ba = sr.generateSeed(4096); + sr.nextBytes(ba); + sr.setSeed(ba); + } + + private static void testNativePRNGImpls() throws Exception { + SecureRandom sr; + byte[] ba; + + System.out.println("Testing new NativePRNGImpls"); + + if (os.startsWith("Windows")) { + System.out.println("Skip windows testing."); + return; + } + + System.out.println(" Testing regular"); + sr = SecureRandom.getInstance("NativePRNG"); + if (!sr.getAlgorithm().equals("NativePRNG")) { + throw new Exception("sr.getAlgorithm(): " + sr.getAlgorithm()); + } + ba = sr.generateSeed(1); + sr.nextBytes(ba); + sr.setSeed(ba); + + System.out.println(" Testing NonBlocking"); + sr = SecureRandom.getInstance("NativePRNGNonBlocking"); + if (!sr.getAlgorithm().equals("NativePRNGNonBlocking")) { + throw new Exception("sr.getAlgorithm(): " + sr.getAlgorithm()); + } + ba = sr.generateSeed(1); + sr.nextBytes(ba); + sr.setSeed(ba); + + if (os.equals("Linux")) { + System.out.println("Skip Linux blocking test."); + return; + } + + System.out.println(" Testing Blocking"); + sr = SecureRandom.getInstance("NativePRNGBlocking"); + if (!sr.getAlgorithm().equals("NativePRNGBlocking")) { + throw new Exception("sr.getAlgorithm(): " + sr.getAlgorithm()); + } + ba = sr.generateSeed(1); + sr.nextBytes(ba); + sr.setSeed(ba); + } + + private static void testStrongInstance(boolean expected) throws Exception { + + boolean result = (SecureRandom.getStrongSecureRandom() != null); + + if (expected != result) { + throw new Exception("Received: " + result); + } + } + + /* + * This test assumes that the standard providers are installed. + */ + private static void testProperty(String property, boolean expected) + throws Exception { + + System.out.println("Testing: '" + property + "' " + expected); + + Security.setProperty("securerandom.strongAlgorithms", property); + testStrongInstance(expected); + } + + private static void testProperties() throws Exception { + // Sets securerandom.strongAlgorithms, and then tests various combos. + testProperty("", false); + + testProperty("SHA1PRNG", true); + testProperty(" SHA1PRNG", true); + testProperty("SHA1PRNG ", true); + testProperty(" SHA1PRNG ", true); + + // Impls are case-insenstive, providers are sensitive. + testProperty("SHA1PRNG:SUN", true); + testProperty("Sha1PRNG:SUN", true); + testProperty("SHA1PRNG:Sun", false); + + testProperty(" SHA1PRNG:SUN", true); + testProperty("SHA1PRNG:SUN ", true); + testProperty(" SHA1PRNG:SUN ", true); + + testProperty(" SHA1PRNG:SUn", false); + testProperty("SHA1PRNG:SUn ", false); + testProperty(" SHA1PRNG:SUn ", false); + + testProperty(",,,SHA1PRNG", true); + testProperty(",,, SHA1PRNG", true); + testProperty(" , , ,SHA1PRNG ", true); + + testProperty(",,,, SHA1PRNG ,,,", true); + testProperty(",,,, SHA1PRNG:SUN ,,,", true); + testProperty(",,,, SHA1PRNG:SUn ,,,", false); + + testProperty(",,,SHA1PRNG:Sun,, SHA1PRNG:SUN", true); + testProperty(",,,Sha1PRNG:Sun, SHA1PRNG:SUN", true); + testProperty(" SHA1PRNG:Sun, Sha1PRNG:Sun,,,,Sha1PRNG:SUN", true); + + testProperty(",,,SHA1PRNG:Sun,, SHA1PRNG:SUn", false); + testProperty(",,,Sha1PRNG:Sun, SHA1PRNG:SUn", false); + testProperty(" SHA1PRNG:Sun, Sha1PRNG:Sun,,,,Sha1PRNG:SUn", false); + + testProperty( + " @#%,%$#:!%^, NativePRNG:Sun, Sha1PRNG:Sun,,Sha1PRNG:SUN", + true); + testProperty(" @#%,%$#!%^, NativePRNG:Sun, Sha1PRNG:Sun,,Sha1PRNG:SUn", + false); + } + + /* + * Linux tends to block, so ignore anything that reads /dev/random. + */ + private static void handleLinuxRead(SecureRandom sr) throws Exception { + if (os.equals("Linux")) { + if (!sr.getAlgorithm().equalsIgnoreCase("NativePRNGBlocking")) { + sr.nextBytes(new byte[34]); + } + } else { + sr.nextBytes(new byte[34]); + sr.generateSeed(34); + sr.setSeed(new byte[34]); + } + } + + /* + * This is duplicating stuff above, but just iterate over all impls + * just in case we missed something. + */ + private static void testAllImpls() throws Exception { + System.out.print("Testing: AllImpls: "); + + Iterator i = Security.getAlgorithms("SecureRandom").iterator(); + + while (i.hasNext()) { + String s = i.next(); + System.out.print("/" + s); + SecureRandom sr = SecureRandom.getInstance(s); + + handleLinuxRead(sr); + handleLinuxRead(sr); + } + System.out.println("/"); + } + + public static void main(String args[]) throws Exception { + testDefaultEgd(); + testSHA1PRNGImpl(); + testNativePRNGImpls(); + testAllImpls(); + + // test default. + testStrongInstance(true); + testProperties(); + } +} diff -r ea7976ed9bc6 -r 0f93bd5cc8d7 test/sun/security/provider/SecureRandom/StrongSeedReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sun/security/provider/SecureRandom/StrongSeedReader.java Thu Apr 11 21:03:24 2013 -0700 @@ -0,0 +1,81 @@ + /* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6425477 + * @summary Better support for generation of high entropy random numbers + * @run main/othervm StrongSeedReader + */ + +import java.io.*; +import java.net.*; +import java.security.SecureRandom; + +/** + * A simple test which takes into account knowledge about the underlying + * implementation. This may change if the implementations change. + * + * Create a new EGD file with known bytes, then set the EGD System property. The + * data read should be the same as what was written. + */ +public class StrongSeedReader { + + public static void main(String[] args) throws Exception { + // Skip Windows, the SHA1PRNG uses CryptGenRandom. + if (System.getProperty("os.name", "unknown").startsWith("Windows")) { + return; + } + + File file = null; + try { + file = new File(System.getProperty("java.io.tmpdir") + + "StrongSeedReader.tmpdata"); + + // write a bunch of 0's to the file. + FileOutputStream fos = new FileOutputStream(file); + fos.write(new byte[2048]); + + System.setProperty("java.security.egd", file.toURI().toString()); + testSeed("NativePRNG"); + testSeed("SHA1PRNG"); + } finally { + if (file != null) { + file.delete(); + } + } + } + + private static void testSeed(String alg) throws Exception { + System.out.println("Testing: " + alg); + SecureRandom sr = SecureRandom.getInstance(alg); + byte[] ba = sr.generateSeed(20); + + // We should get back a bunch of zeros from the file. + for (byte b : ba) { + if (b != 0) { + throw new Exception("Byte != 0"); + } + } + } +}