Random random = new Random();
random.setSeed(new SecureRandom().nextInt());
random.nextInt();
是乱写的,还是有什么考虑?请大神指点一二。
1
chendy 2023-01-11 17:15:37 +08:00
随机基础上再随机…
其实直接 new SecureRandom().nextInt() 就行了 因为种子一样随机的序列也是一样的,所以外面这一层 Random |
3
Ediacaran 2023-01-11 17:16:04 +08:00 via iPhone
种子足够随机
|
6
sunny352787 2023-01-11 17:20:51 +08:00
总得给个种子吧,至于种子来源其实无所谓
常用的当前时间做种子理论上来说确实有被攻击的可能,所以这样写算是更安全的写法 不过实际上我觉得这人是想的有点多,除非是纯客户端程序在本地包个虚拟机强行改时间,不然的话用服务器当前时间戳作为随机数种子我觉得没啥问题 |
7
sunny352787 2023-01-11 17:24:30 +08:00
@jaredyam 通常的想法是 Random 要比 SecureRandom 性能高,SecureRandom 比 Random 更安全,这样用应该类似 https 的设计,用高安全性的 RSA 来传递密钥,然后用高性能的对称算法来执行加密过程,基本都是这种设计思路
|
8
CEBBCAT 2023-01-11 17:27:17 +08:00 1
我推测是希望获得安全的随机数,但又希望减少来自 SecureRandom 的阻塞。
但如果说要评价写得好不好,应该在完成设计目标的前提下,考察随机性和必要性。即 SecureRandom().nextInt() 是否带来了足够的墒、new Random()的函数是不是安全的。以及可不可以直接使用 SecureRandom().nextInt()。 |
9
jaredyam OP |
10
ysc3839 2023-01-11 17:28:17 +08:00 via Android
SecureRandom 应该是用系统的安全随机数,Random 应该是纯算法的伪随机数。
个人感觉这么做,如果后面是从 random 对象中取多个随机数的话,会降低安全性。如果只取一次问题不大。 |
11
sunny352787 2023-01-11 17:28:24 +08:00
@jaredyam 另外,SecureRandom 本身在 Linux 等系统下使用的是操作系统的熵计算,也就是真随机数,但熵源不足的情况下会造成阻塞
|
12
ipwx 2023-01-11 17:29:34 +08:00
SecureRandom 的默认参数所使用的种子可能来自于( Posix 上)/dev/random 。而 /dev/random 是个有限的真随机序列,其内容来自于对机器硬件数据的采集。比如网卡的噪音、温度传感器、鼠标键盘动作等。
通过 SecureRandom 得到一个普通伪随机生成器 Random 的初始 seed ,可以有效地得到一个不可预测的随机序列。 |
13
sunny352787 2023-01-11 17:31:57 +08:00 1
@jaredyam 过程没问题,而且确实比用时间做种子更安全
大部分的语言的默认随机数生成器的实现都是需要设置一个种子的,不然默认种子为 0 ,也就是每次启动程序随机出来的序列都是一模一样的 大部分随机数生成算法就是一个迭代公式,用上一个结果计算下一个随机数,那么第一个数从哪来呢?就是你给的种子呗 |
14
tinybaby365 2023-01-11 17:35:10 +08:00
random.nextInt();只一次,不如 new SecureRandom().nextInt()
random.nextInt();有限次,挺好 random.nextInt();很多次,看实际情况判断 |
15
CEBBCAT 2023-01-11 17:37:23 +08:00
虽然还没人问,但我推荐阅读一下 /dev/urandom 和 /dev/random 的文章,好像和前几年那些 Linux 书本不同,现在即使在加密场景下,也推荐使用 /dev/urandom ,说是已经足够安全了,又不会阻塞。
|
16
h0099 2023-01-11 17:42:46 +08:00
|
17
zankard 2023-01-11 18:11:56 +08:00 1
https://metebalci.com/blog/everything-about-javas-securerandom/ 可以看看这个,使用 random 看你对安全性的要求,里面有一个破解程序,只需要知道连续两个随机数,很快就能算出 seed 了,这时候使用 securerandom 作为 seed 与否并不不能保证安全的。
|
19
realpg 2023-01-11 19:36:27 +08:00
不搞密码学应用 不用考虑这些问题
在座诸位 99.999999%用随机数的场景都只是需要一个随机一点的值来实现逻辑,而不是基于随机性进行安全化 |
20
kkhaike 2023-01-11 21:16:00 +08:00
这是对的。SecureRandom 应该是使用系统熵的真随机。使用这个作为伪随机的种子能够做到更好的效果。
因为在分布式 /多实例场景下,使用时间作为种子也很容易得到初冲突。 c++11 后也是这样的,参考 https://learn.microsoft.com/zh-tw/cpp/standard-library/random?view=msvc-170 最后一个实例 go 也有使用 crypto/rand 设置 math/rand 种子的用法 主要原因是真随机的获取非常慢。不适合在业务场景下使用。这种方式能够兼得随机性与速度的最好效果(甚至可以在多线程场景下分线程创建以避免锁开支) |
21
CEBBCAT 2023-01-11 23:32:25 +08:00
@kkhaike #20 单论 Golang 来说,crypto/rand 的速度没有非常慢,大概是 math/rand 的一半。同样是读取 4KB 随机字节,crypto 花费 7.5us ,math 花费 3.3us 。
goos: darwin goarch: arm64 BenchmarkRandRead4KB-8 356580 3335 ns/op BenchmarkCryptoRead4KB-8 158610 7540 ns/op https://gist.github.com/Zhang-Siyang/cb10162e8f98e87041201d15aea89088 |
22
Avn 2023-01-12 09:33:58 +08:00
seed 相同的 Random 产生的序列也是相同的
```java public static void main(String[] args) { Random random1 = new Random(); random1.setSeed(1L); Random random2 = new Random(); random2.setSeed(1L); for (int i = 0; i < 3; i++) { System.out.println("random 1 - " + random1.nextInt()); System.out.println("random 2 - " + random2.nextInt()); } } ``` ```shell random 1 - -1155869325 random 2 - -1155869325 random 1 - 431529176 random 2 - 431529176 random 1 - 1761283695 random 2 - 1761283695 ``` 把 seed 改成随机数可以避免这个现象 |
23
wangyu17455 2023-01-12 10:13:48 +08:00
这实际上就是 /dev/urandom 的随机数生成方式,用 securerandom 做种子,然后跑纯靠计算的随机数算法
|
24
h0099 2023-01-12 20:23:16 +08:00
#23 @wangyu17455 https://www.2uo.de/myths-about-urandom/ 早已指出(机翻):
/dev/urandom 是一个伪随机数生成器,一个 PRNG ,而 /dev/random 是一个“真”随机数生成器。 事实: /dev/urandom 和 /dev/random 都使用完全相同的 CSPRNG (一种加密安全的伪随机数生成器)。它们仅在极少数方面有所不同,与“真正的”随机性无关。 /dev/random 无疑是密码学的更好选择。即使 /dev/urandom 相对安全,也没有理由选择后者。 --- 因此,要明确一件事:/dev/random 和 /dev/urandom 都由同一个 CSPRNG 提供。根据一些估计,只有当它们各自的池耗尽熵时的行为不同:/dev/random 阻塞,而 /dev/urandom 没有。 从 Linux 4.8 开始 在 Linux 4.8 中,/dev/urandom 和 /dev/random 之间的等效性被放弃了。现在 /dev/urandom 输出不是来自熵池,而是直接来自 CSPRNG 。 |
25
h0099 2023-01-12 20:24:50 +08:00
/dev/random 无疑是密码学的更好选择。即使 /dev/urandom 相对安全,也没有理由选择后者。
事实: /dev/random 有一个非常讨厌的问题:它会阻塞。 但这很好!/dev/random 给出的随机性与其池中的熵一样多。/dev/urandom 会给你不安全的随机数,即使它早已耗尽熵。 事实:不。即使不考虑可用性和随后的用户操纵等问题,熵“耗尽”的问题也是一个稻草人。大约 256 位的熵足以在很长很长一段时间内获得计算上安全的数字。 |
26
kkhaike 2023-01-13 12:27:27 +08:00
我说的是 真随机 的获取非常慢,因为涉及到操作系统要主动将熵值填入熵池,这个是很缓慢的。
golang 的 crypt/rand 在某些系统下并不是纯正的真随机(纯真随机应该类似于 /dev/random 一样会在熵池被掏空时阻塞),golang 在不同的操作系统下的实现 // On Linux, FreeBSD, Dragonfly and Solaris, Reader uses getrandom(2) if // available, /dev/urandom otherwise. // On OpenBSD and macOS, Reader uses getentropy(2). // On other Unix-like systems, Reader reads from /dev/urandom. // On Windows systems, Reader uses the RtlGenRandom API. 1. getrandom(flags==0), 相当于 /dev/random ,https://github.com/torvalds/linux/blob/d9fc1511728c15df49ff18e49a494d00f78b7cd4/drivers/char/random.c#L1343-L1350 ,默认会阻塞等待熵值 2. getentropy ,https://support.apple.com/zh-cn/guide/security/seca0c73a75b/web , 使用 Fortuna 算法,安全度很高的伪随机并使用熵源初始化,无阻塞 3. RtlGenRandom ,https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom , 文档上就说是伪随机 4. /dev/urandom ,不会阻塞,会在熵值耗尽使用 crng_fast_key_erasure 获得伪随机结果( chacha 加密结果的一部分) https://github.com/torvalds/linux/blob/d9fc1511728c15df49ff18e49a494d00f78b7cd4/drivers/char/random.c#L336-L341 |
27
kkhaike 2023-01-13 12:35:58 +08:00
另外真随机是完全无规律的随机,所以只适合用作密码学相关以及设置伪随机的种子,在业务场景下,希望获得 均匀分布、正态分布 等特性的随机数需要对应的伪随机配合,所以 业务场景 真随机种子 + 伪随机算法 就是最优解没有之一
|