繁体   English   中英

Java SecureRandom在创建和重新播种方面的正确用法

[英]Java SecureRandom correct usage around creation and reseeding

我们有一项业务要求,以生成随机的临时密码。 根据使用情况,此类呼叫的数量预计会非常低(每天约400个呼叫)。 根据Internet上的各种建议,并且在阅读了SO上的许多类似文章之后,我们决定使用java.security.SecureRandom来实现具有加密强度的随机化。

现在,我们已经编写了一个简单的随机化器(内部使用SecureRandom ),应该将其用作Web应用程序中的singleton 但是,根据SO的建议,我们也将定期重新播种它。 为此,下面是一些实现此目的的示例代码。 有人可以复查一下,让我们知道这是否是正确且合理的方法吗? 另外,有没有办法在保持线程安全的同时避免代码synchronization ?:

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

}

您可以根据调用次数来重新设定种子,而不是基于时间。

在该类中维护一个计数器,并在每次调用随机生成器时增加它。 当计数器达到某个阈值时,将其重新设定为种子并将其初始化为0。您可以每100万次调用重新设定种子。

这是我唯一可以建议的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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