您可以“克隆” java SecureRandom对象吗? -如何获得可克隆的强RNG?

时间:2020-02-01 13:33:24

标签: java random clone entropy

我有一个需要较长随机位序列的应用程序。

因为我依赖这些序列是“真正随机的”,所以我想使用SecureRandom。

遗憾的是,每个这样的序列我都需要两次。

我无法将它们保留在RAM中,因为它们太长了。

我不想将它们写入硬盘驱动器,因为向硬盘驱动器写入和读取会浪费时间和硬盘驱动器空间,并使代码不必要地变得复杂。

但是,由于某些生成器生成的“随机”位序列确定性地取决于生成器在创建时所处的状态,因此一个(也许)可以保存并重新创建新生成的SecureRandom对象所处的状态。 ,允许增加位序列。

主要问题是:

(如何)可以保存SecureRandom随机数生成器的整个状态?克隆够了吗? (可能会有一些OS状态也起作用,例如当前时间)。您还可以克隆其他强大的RNG以便原始副本和克隆副本创建相同的输出吗?

此外,我对正常的随机数生成器会创建哪种类型的模式,使其变得不安全以及是否可以如上所述保存和恢复其状态感兴趣。

编辑:

我为此创建了一个LCG。

但是,我认为它无法正常工作。

它会创建一个“强”的伪随机位序列吗?

当它连续多次产生相同的值时,就变得可疑了。 例如:

假 假 真正 真正 真正 真正 假 真正 假 假 假 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 是

public class DeterministicRandom{
    private BigInteger state;
    private BigInteger a;
    private BigInteger c;
    private static final int o=32;
    private static final BigInteger m=BigInteger.ONE.shiftLeft(o*8);
    public DeterministicRandom(){
        a=RandomUtils.randomBigInt(o);
        c=RandomUtils.randomBigInt(o);
        state=RandomUtils.randomBigInt(o);
    }
    public DeterministicRandom(DeterministicRandom r){
        a=r.a;
        c=r.c;
        state=r.state;
    }
    public boolean nextBoolean(){
        boolean r=state.testBit(o*8-7);
        state=state.multiply(a).add(c).mod(m);
        return r;
    }
}
public class RandomUtils {
    private static Random random = new Random();
    public static byte[] nextBytes(byte[] d){
        random.nextBytes(d);
        return d;
    }
    public boolean randomBit(){
        return random.nextBoolean();
    }
    public static BigInteger randomBigInt(int bytes){
        byte[] d=new byte[bytes+1];
        random.nextBytes(d);
        d[0]=0;
        BigInteger x=new BigInteger(d);
        return x;
    }
}

2 个答案:

答案 0 :(得分:3)

似乎您需要用于机器学习或蒙特卡洛采样的随机数。您似乎还考虑了java.util.Random,但发现它不适合您的目的。在这种情况下,SecureRandom远不是最佳选择。特别是,没有实现SecureRandom的“ SHA1PRNG”提供程序的标准方法,并且在应用程序关心可重现的“随机性”的情况下,不应使用SecureRandom。另请参见Encryption algorithm giving different results on Android 2.1 and versions above 2.1

相反,您需要一种高质量PRNG ,并且有许多high-quality RNG algorithms可用。

在Java中,两个examples包含it.unimi.dsi/dsiutilsorg.apache.commons/commons-rng-simple工件,其中包括可植入PRNG的实现,例如xoroshiro128++xoshiro256**

答案 1 :(得分:2)

我认为您一般不能依赖 来克隆SecureRandom实例,也不能依赖提供与原始序列相同的克隆。

问题是典型的Java平台提供了安全随机性的多种实现,并且当您调用new SecureRandom()时会获得默认的实现。这可能是PRNG,也可能是使用“真实”随机性源的RNG。在后一种情况下,即使类支持clone(),克隆对象也不会给出与原始对象相同的随机序列。

我通常会想到几种解决方案。


第一个解决方案是创建包装Random实例的自己的SecureRandom类。特殊的调味料是您的包装器类需要记录它发出的随机数流。然后,您需要一种reset()方法,使PRNG切换为使用记录的号码。

缺点是您需要足够的内存来容纳所有数字。 (取决于表示形式,这可能会占用很多内存。例如,如果您在ArrayList<Integer>中记录N个整数,则最坏的情况是Integer对象的N x 24字节,峰值为3 ArrayList的x N x 8个字节。int[]的密度更高,但更难管理。)

我看到您已经说过序列太长而无法保存在您的用例中。您可以考虑将它们写入文件,但是即使那样也不会无限扩展。


第二种解决方案是利用SecureRandom类的能力来选择算法并重新设定种子本身。

SecureRandom seeder = new SecureRandom();
byte[] seed = seeder.generateSeed(NOS_BYTES);

SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
prng.setSeed(seed);

SecureRandom prng2 = SecureRandom.getInstance("SHA1PRNG");
prng2.setSeed(seed);

// prng1 and prng2 created as above should generate the same sequences

NOS_BYTES设置为要使用的种子的字节数。或者,您可以“手动”生成种子,也可以从文件中读取它。

请注意,您不能像使用new SecureRandom()获得的实例那样可靠地设置种子,也不能可靠地重新种子现有实例 1

根据配置的Java平台安全提供程序,可能还会使用其他PRNG算法。

有关更多信息,请参阅SecureRandom javadoc

我测试了上述方法,并在使用Java 11的计算机上,prngprng2对象生成了相同的int值序列...直到我击中^ C。 / p>

1-问题的症结在于setSeed的javadoc这样说:“给定的种子补充而不是替换了现有的种子。” 。在其他地方,它表示构造函数返回已播种的对象。幸运的是,它还说getInstance方法返回了尚未种子的对象。


最后,我从您更新的问题中看到,您正在考虑使用通过BigInteger实现的“自行拥有” PRNG。

当心!

实现不如您想像的PRNG非常容易。我不会那样做。但是,如果您决定走这条路,那么您应该通读PRNG的理论及其属性,并对实现进行一系列(相关的)统计测试。

(如果您打算发表结果,您的论文应该提到您实现了自己的PRNG,并且应该使PRNG源代码和统计测试结果可供读者检查。出于公开和科学的考虑可复制性。我认为。)