这段随机数生成代码为什么这么写? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jaredyam
V2EX    Java

这段随机数生成代码为什么这么写?

  •  
  •   jaredyam 2023-01-11 17:12:03 +08:00 4348 次点击
    这是一个创建于 1092 天前的主题,其中的信息可能已经有所发展或是发生改变。
    Random random = new Random(); random.setSeed(new SecureRandom().nextInt()); random.nextInt(); 

    是乱写的,还是有什么考虑?请大神指点一二。

    28 条回复    2023-01-13 12:39:32 +08:00
    chendy
        1
    chendy  
       2023-01-11 17:15:37 +08:00
    随机基础上再随机…
    其实直接 new SecureRandom().nextInt() 就行了
    因为种子一样随机的序列也是一样的,所以外面这一层 Random
    chendy
        2
    chendy  
       2023-01-11 17:15:54 +08:00
    @chendy #1 所以外面这一层 Random 没用了…
    Ediacaran
        3
    Ediacaran  
       2023-01-11 17:16:04 +08:00 via iPhone
    种子足够随机
    jaredyam
        4
    jaredyam  
    OP
       2023-01-11 17:18:55 +08:00
    @Ediacaran 原来这块代码种子的随机数位数也要更大些
    jaredyam
        5
    jaredyam  
    OP
       2023-01-11 17:19:43 +08:00
    @chendy 我现在是没想通它这个加层种子是想干啥,而且一个是 Random ,一个又是 Secure 的
    sunny352787
        6
    sunny352787  
       2023-01-11 17:20:51 +08:00
    总得给个种子吧,至于种子来源其实无所谓

    常用的当前时间做种子理论上来说确实有被攻击的可能,所以这样写算是更安全的写法
    不过实际上我觉得这人是想的有点多,除非是纯客户端程序在本地包个虚拟机强行改时间,不然的话用服务器当前时间戳作为随机数种子我觉得没啥问题
    sunny352787
        7
    sunny352787  
       2023-01-11 17:24:30 +08:00
    @jaredyam 通常的想法是 Random 要比 SecureRandom 性能高,SecureRandom 比 Random 更安全,这样用应该类似 https 的设计,用高安全性的 RSA 来传递密钥,然后用高性能的对称算法来执行加密过,基本都是这种设计思路
    CEBBCAT
        8
    CEBBCAT  
       2023-01-11 17:27:17 +08:00   1
    我推测是希望获得安全的随机数,但又希望减少来自 SecureRandom 的阻塞。

    但如果说要评价写得好不好,应该在完成设计目标的前提下,考察随机性和必要性。即 SecureRandom().nextInt() 是否带来了足够的墒、new Random()的函数是不是安全的。以及可不可以直接使用 SecureRandom().nextInt()。
    jaredyam
        9
    jaredyam  
    OP
       2023-01-11 17:27:57 +08:00
    @sunny352787 所以你是说这个过程没问题是吧。

    > 总得给个种子吧

    这句话可以再解释下吗?和直接 Random.nextInt()有什么区别呢?也是出于安全的考虑?
    ysc3839
        10
    ysc3839  
       2023-01-11 17:28:17 +08:00 via Android
    SecureRandom 应该是用系统的安全随机数,Random 应该是纯算法的伪随机数。
    个人感觉这么做,如果后面是从 random 对象中取多个随机数的话,会降低安全性。如果只取一次问题不大。
    sunny352787
        11
    sunny352787  
       2023-01-11 17:28:24 +08:00
    @jaredyam 另外,SecureRandom 本身在 Linux 等系统下使用的是操作系统的熵计算,也就是真随机数,但熵源不足的情况下会造成阻塞
    ipwx
        12
    ipwx  
       2023-01-11 17:29:34 +08:00
    SecureRandom 的默认参数所使用的种子可能来自于( Posix 上)/dev/random 。而 /dev/random 是个有限的真随机序列,其内容来自于对机器硬件数据的采集。比如网卡的噪音、温度传感器、鼠标键盘动作等。

    通过 SecureRandom 得到一个普通伪随机生成器 Random 的初始 seed ,可以有效地得到一个不可预测的随机序列。
    sunny352787
        13
    sunny352787  
       2023-01-11 17:31:57 +08:00   1
    @jaredyam 过程没问题,而且确实比用时间做种子更安全

    大部分的语言的默认随机数生成器的实现都是需要设置一个种子的,不然默认种子为 0 ,也就是每次启动程序随机出来的序列都是一模一样的
    大部分随机数生成算法就是一个迭代公式,用上一个结果计算下一个随机数,那么第一个数从哪来呢?就是你给的种子呗
    tinybaby365
        14
    tinybaby365  
       2023-01-11 17:35:10 +08:00
    random.nextInt();只一次,不如 new SecureRandom().nextInt()

    random.nextInt();有限次,挺好

    random.nextInt();很多次,看实际情况判断
    CEBBCAT
        15
    CEBBCAT  
       2023-01-11 17:37:23 +08:00
    虽然还没人问,但我推荐阅读一下 /dev/urandom 和 /dev/random 的文章,好像和前几年那些 Linux 书本不同,现在即使在加密场景下,也推荐使用 /dev/urandom ,说是已经足够安全了,又不会阻塞。
    zankard
        17
    zankard  
       2023-01-11 18:11:56 +08:00   1
    https://metebalci.com/blog/everything-about-javas-securerandom/ 可以看看这个,使用 random 看你对安全性的要求,里面有一个破解程序,只需要知道连续两个随机数,很快就能算出 seed 了,这时候使用 securerandom 作为 seed 与否并不不能保证安全的。
    zankard
        18
    zankard  
       2023-01-11 18:19:46 +08:00
    @zankard 不是连续的随机数,应该也可以运算,就是计算量就更大了。
    realpg
        19
    realpg  
    PRO
       2023-01-11 19:36:27 +08:00
    不搞密码学应用 不用考虑这些问题

    在座诸位 99.999999%用随机数的场景都只是需要一个随机一点的值来实现逻辑,而不是基于随机性进行安全化
    kkhaike
        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 种子的用法

    主要原因是真随机的获取非常慢。不适合在业务场景下使用。这种方式能够兼得随机性与速度的最好效果(甚至可以在多线程场景下分线程创建以避免锁开支)
    CEBBCAT
        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
    Avn
        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 改成随机数可以避免这个现象
    wangyu17455
        23
    wangyu17455  
       2023-01-12 10:13:48 +08:00
    这实际上就是 /dev/urandom 的随机数生成方式,用 securerandom 做种子,然后跑纯靠计算的随机数算法
    n0099
        24
    n0099  
       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 。
    n0099
        25
    n0099  
       2023-01-12 20:24:50 +08:00
    /dev/random 无疑是密码学的更好选择。即使 /dev/urandom 相对安全,也没有理由选择后者。

    事实: /dev/random 有一个非常讨厌的问题:它会阻塞。

    但这很好!/dev/random 给出的随机性与其池中的熵一样多。/dev/urandom 会给你不安全的随机数,即使它早已耗尽熵。

    事实:不。即使不考虑可用性和随后的用户操纵等问题,熵“耗尽”的问题也是一个稻草人。大约 256 位的熵足以在很长很长一段时间内获得计算上安全的数字。
    kkhaike
        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
    kkhaike
        27
    kkhaike  
       2023-01-13 12:35:58 +08:00
    另外真随机是完全无规律的随机,所以只适合用作密码学相关以及设置伪随机的种子,在业务场景下,希望获得 均匀分布、正态分布 等特性的随机数需要对应的伪随机配合,所以 业务场景 真随机种子 + 伪随机算法 就是最优解没有之一
    kkhaike
        28
    kkhaike  
       2023-01-13 12:39:32 +08:00
    @CEBBCAT 上面忘了 at 。。。不好意思
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5665 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 02:40 PVG 10:40 LAX 18:40 JFK 21:40
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86