【问题标题】:Can you "clone" a java SecureRandom object? - How to get a cloneable strong RNG?你能“克隆”一个java SecureRandom 对象吗? - 如何获得可克隆的强RNG?
【发布时间】:2020-02-01 13:33:24
【问题描述】:

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

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

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

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

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

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

主要问题是:

(如何)你能保存 SecureRandom 随机数生成器的整个状态吗?克隆够吗? (可能有一些操作系统状态也起作用,例如,当前时间)您是否可以克隆任何其他强 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;
    }
}

【问题讨论】:

  • 一种方法是使用SecureRandom 生成种子,然后使用该种子创建两个常规Random 实例。
  • 那么,随机“随机”就够了吗?随机是“确定性的”吗? “种子生成:随机使用系统时钟作为种子/或生成种子。”-geeksforgeeks.org/random-vs-secure-random-numbers-java上述问题的答案是否取决于实现?我真的必须确保序列完全按照第一次生成的方式重新生成。
  • SecureRandom 生成真正的随机种子。如果你用它来播种你的两个Random 实例,它们将产生相同的伪随机确定性序列。如果您想要巨大的真正随机序列,生成它们可能比从磁盘读取它们要慢,因为收集熵数据往往很慢。您请求的所有信息都在 JavaDoc 及其引用的规范中。
  • 你不能同时拥有。真正的随机性与确定性截然相反。
  • 你想要的叫做伪随机数生成器,Java already has one built in。除非您可以在数学上证明您需要 更好 随机数序列生成,否则您根本不需要 SecureRandom。只需使用 Random,只有当您确实有证据不够好时,才开始寻找更好的 PRNG。

标签: java random clone entropy


【解决方案1】:

您似乎想要机器学习或蒙特卡洛采样的随机数。您似乎也考虑过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**

【讨论】:

    【解决方案2】:

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

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

    我能想到几个解决方案(一般来说)。


    第一个解决方案是创建您自己的 Random 类来包装 SecureRandom 实例。特殊之处在于您的包装类需要记录它发出的随机数流。然后你需要一个reset() 方法来让你的 PRNG 切换到使用记录的数字。

    缺点是您需要足够的内存来保存所有数字。 (根据表示,这可能会占用大量内存。例如,如果您在 ArrayList<Integer> 中记录 N 个整数,最坏的情况是 Integer 对象的 N x 24 字节和 3 x N x 8 的峰值ArrayList 的字节数。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

    您可能还可以使用其他 PRNG 算法,具体取决于您的 Java 平台配置的安全提供程序。

    更多信息请参考SecureRandomjavadoc

    我测试了上述方法,在我的机器上使用 Java 11,prngprng2 对象生成了相同的int 值序列......直到我点击 ^C。

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


    最后,我从您更新的问题中看到,您正在考虑使用使用 BigInteger 实现的“roll-your-own”PRNG。

    小心!

    很容易实现一个没有你想象的那么好的 PRNG。我不会那样做的。但是如果你决定走这条路,你应该阅读 PRNG 的理论及其属性,并对你的实现运行一批(相关的)统计测试。

    (如果你打算发表你的结果,你的论文应该提到你实现了自己的PRNG,并且你应该将PRNG源代码和统计测试结果提供给读者查看。为了开放和科学可重复性。在我看来。)

    【讨论】:

    • 我使用 SecureRandom 测试了您的提案。在我的设备上它也工作得很好!另外,我真的很喜欢你解释为什么这样做的方式,并且你指出了解决这个问题所涉及的问题的几种方法。非常感谢您!
    猜你喜欢
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    相关资源
    最近更新 更多