简体   繁体   中英

Java SecureRandom correct usage around creation and reseeding

We have a business requirement to generate random temporary passwords. As per the use case, the volume of such calls is expected to be very low (~400 calls/day). We've decided to use java.security.SecureRandom to achieve cryptographically strong randomization, as per various recommendations on the Internet, and after reading many similar posts on SO.

Now, we've written a simple randomizer (internally using the SecureRandom ), which is supposed to be used as a singleton within the web application. However, we would also periodically want to reseed it, again as per recommendations on SO. To that end, below is some sample code that achieves the same. Can someone please review it and let us know if this is the right and reasonably efficient approach? Also, is there a way to avoid the synchronization in the code, while still maintaining thread safety?:

import java.security.*;
public final class Randomizer {
    private static final Randomizer INSTANCE = new Randomizer();

    private static final String DEFAULT_CSPRNG_ALGO = "SHA1PRNG";

    private volatile SecureRandom sr;
    private volatile long lastSeedTime;

    public static final Randomizer getInstance() throws Exception {
        return INSTANCE;
    }

    public int nextInt(int len) throws RuntimeException {
        reseedRandomAsNeeded();
        return sr.nextInt(len);
    }

    private Randomizer() throws RuntimeException {
        try {
                System.out.printf("%s Constructing Randomizer...%n", Thread.currentThread());
                recreateSecureRandomInstance();
                lastSeedTime = System.nanoTime();
        } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
        }
    }

    /**
     * TODO Is there a way to avoid the synchronization overhead here? We really
     * only need to synchronize when the reseed happens.
     * 
     * @throws RuntimeException
     */
    private synchronized void reseedRandomAsNeeded() throws RuntimeException {
        if (isItTimeToReseed()) {
                // TODO Need to do a reseed. Just get a new SecureRandom for now.
                try {
                        recreateSecureRandomInstance();
                } catch (NoSuchAlgorithmException e) {
                        throw new RuntimeException(e);
                }
        }
    }

    private boolean isItTimeToReseed() {
        boolean reseed = false;
        long currentTime = System.nanoTime();
        long difference = ((currentTime - this.lastSeedTime) / (1000 * 1000 * 1000)/* *60 * 60 * 24*/);
        // System.out.printf("%s Current time: %d, Last Reseed Time: %d, difference: %d%n",
        // Thread.currentThread(), currentTime, lastSeedTime, difference);

        // TODO For testing, test for just a 3 seconds difference.
        if (difference > 3) {
                reseed = true;
                this.lastSeedTime = currentTime;
        }

        return reseed;
    }

    private void recreateSecureRandomInstance() throws NoSuchAlgorithmException {
        sr = SecureRandom.getInstance(DEFAULT_CSPRNG_ALGO);
        System.out.printf("%s Created a new SecureRandom instance: %s%n", Thread.currentThread(), sr);
    }

}

Instead of time based, you can reseed based on number of invocations.

Maintain a counter in the class and increase it every time the random generator is called. When the counter reaches some threshold, reseed it and initialize count to to 0. You can reseed say for every 1 million invocations.

That is the only thing I can suggest.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM