跨版本和平台的一致随机数

3

我需要/想要获得随机(不完全是)数字用于密码生成。

我的做法:目前我使用SecureRandom来生成它们。
我用以下方式获取对象:

SecureRandom sec = SecureRandom.getInstance("SHA1PRNG", "SUN");

然后像这样进行播种
sec.setSeed(seed);

目标: 快速创建随机数的方法,要求在加密上至少与SHA1PRNG SecureRandom实现同样安全,并且在不同版本的JRE和Android上生成的随机数相同。

EDIT: 种子是由用户输入生成的。

问题: 使用SecureRandom.getInstance("SHA1PRNG", "SUN");会出现以下错误:java.security.NoSuchProviderException: SUN。如果省略, "SUN"则会生成随机数,但这些随机数与默认的(JRE 7)随机数不同。

问题: 我应该如何达到我的目标

您不希望它是可预测的: 我需要可预测性,以便相同的先决条件产生相同的输出。如果它们不同,对于应用程序用户的期望,这就是不可能的 困难的

EDIT: 可预测性指的是,当了解一个或一百个字节时,不能预测下一个,但当您知道种子时,应该能够预测第一个(以及所有其他随机数)。或者用另一个词来说,是可重现的

如果有人知道更直观的方法,请告诉我!


它们要么是可预测的,要么不可预测。如果你想让它们可预测,使用相同的种子。但只要种子被共享,攻击者就有办法获取它。 - etienne
种子不是共享的,我不知道它,只有用户知道它。因此,如果用户输入相同的种子,则应该输出相同的随机数。 - tilpner
在写帖子之前,我尝试过这个(已经写在里面了),但输出确实不同... - tilpner
听起来你想使用哈希算法而不是随机数生成器。https://dev59.com/n3I_5IYBdhLWcg3wHvU5 - Zim-Zam O'Pootertoot
为了将来的参考,请注意,此问题的正确答案可在https://dev59.com/Emox5IYBdhLWcg3whUqm找到。 - Erich Eichinger
显示剩余10条评论
4个回答

1

我最终从sun源代码中分离出了Sha1Prng,这保证了在所有版本的Java和Android上的可重复性。为了确保与Android兼容性,我需要放弃一些重要的方法,因为Android无法访问nio类...


0

如果你想要可预测性,那它们就不是随机的。这会破坏你“目标”的“安全”要求,并演变成两个服务器之间的简单共享密钥。

你可以通过使用质数的特征来构建一组整数,从而获得看起来有点随机但是可预测的结果。你可以从一个特定的整数I开始,加上第一个质数,然后对第二个质数取模。实际上,第一个和第二个数字只需要是相对质数--也就是说,它们没有共同的质因数--不包括1,如果你把1算作因数的话。

如果你重复添加和取模的过程,你将得到一组数字,你可以重复地产生它们,并且它们是有序的。也就是说,取集合中的任何一个成员,加上第一个质数并对第二个质数取模,你总是会得到相同的结果。

最后,如果第一个质数相对于第二个质数很大,那么这个序列对人类来说就不容易预测,看起来有点随机。

例如,第一个质数=7,第二个质数=5(请注意,这只是展示它如何工作,而在现实生活中并不实用)

从2开始。加7得到9。模5得到4。 4加7等于11。模5等于1。

序列是2、4、1、3、0,然后重复。

现在是为了生成看似随机的数字。相对质数是91193和65536。(我选择第二个因为它是2的幂,所以所有模值都可以适应16位。)

int first = 91193;
int modByLogicalAnd = 0xFFFF;

int nonRandomNumber = 2345; // Use something else
for (int i = 0; i < 1000 ; ++i) {
    nonRandomNumber += first;
    nonRandomNumber &= modByLogicalAnd;
    // print it here
}

每次迭代会生成2个字节的伪随机数。如果需要更大的随机“字符串”,您可以将多个随机数打包到缓冲区中。

它们是可重复的。您的用户可以选择起始点,您可以使用任何质数(或者实际上是没有2作为因数的任何数字)。

顺便说一下 - 使用2的幂作为第二个数字会使其更可预测。


我编辑了我的帖子:种子是基于用户输入生成的。此外:我怀疑这不像SHA1PRNG那样具有密码学安全性。从一个字节到下一个字节的猜测可能是不可预测的,但是当您知道种子时,第一个字节应该是可预测的... - tilpner
我认为,你可能不同意,但我坚持认为,想要使其可重复的方式最多只能让“密码学安全”成为一种幻觉。 - Lee Meador
让我们称其为“可重现”,而不是“重复”。 - tilpner

0

我建议使用UUID.randomUUID(),然后使用getLeastSignificantBits()和getMostSignificantBits()将其拆分为长整型。


1
这不是我需要的。目前(或者我还没有看到)似乎没有办法让它输出相同的内容多次。 - tilpner

0

忽略使用某些物理输入(随机时钟位、电噪声等)的 RNG,所有软件 RNG 都是可预测的,只要给定相同的起始条件。毕竟,它们是(希望如此)确定性计算机程序。

有一些算法故意包括物理输入(例如,偶尔采样计算机时钟)以尝试防止可预测性,但这些算法(据我所知)是例外。

因此,任何“传统”的 RNG,在给定相同的种子并按照相同规范实现的情况下,应该产生相同的“随机”数序列。(这就是为什么计算机 RNG 更恰当地称为“伪随机数生成器”的原因。)

一个 RNG 可以使用先前使用过的种子进行初始化,并再次生成“已知”的数字序列,这并不会使 RNG 比某些无法进行种子初始化的 RNG 不安全(尽管它可能比那些在间隔时间内重新进行种子初始化的高级算法不安全)。而且,能够这样做——一遍又一遍地重复相同的序列,在测试中不仅非常有用,而且在加密和其他安全应用中也有一些“真实生活”应用。(事实上,加密算法本质上就是一个可重复的随机数生成器。)


这是应该作为一个答案吗?(不想听起来愚蠢,但这些对我来说都不是新的,也没有帮助到我的问题)你把你的评论包含进来怎么样? - tilpner
@StackOverflowException - 基本上它只是一个太长而无法放入评论中的注释。 - Hot Licks
抱歉,在阅读这个“评论-发布”的评论后,我才看到你的“评论”。 - tilpner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接