Java SecureRandom 不阻塞?如何?

信息安全 linux 爪哇 随机的
2021-08-20 08:52:45

我从经验中知道,当 Linux 内核熵池用完熵时,从 /dev/random 中读取会阻塞。此外,我看到许多文章和博客文章指出,在 Linux 上运行时,java.security.SecureRandom 使用 /dev/random 作为其熵源,因此当内核熵池用完熵时会阻塞。

但是,我无法进行导致 SecureRandom 阻塞的实验。相反,似乎很容易获得一个从 /dev/random 读取的简单 bash 单行代码来阻止。

这是我用于这些实验的 java 代码:

import java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr = new SecureRandom();
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}

它生成超过 1,000,000 个随机 32 位整数。那应该是 2^(20 + log2(32)) = 2^25 位或 2^22(略超过 400 万)字节的熵,对吧?但是,它从不阻塞。无论我是否摆动鼠标,它总是在大约 1.2 秒内完成。

我使用的 bash 单线是:

head -c 100 /dev/random | xxd

这很容易阻塞。只要我把手从鼠标和键盘上移开,它就会在那里闲置几分钟。我只要求 100 字节的熵。

当然,我在这里遗漏了一些东西。有人可以解释发生了什么吗?

谢谢!

4个回答

至少在我测试的机器上(OpenJDK JRE 6b27 和 Sun JRE 6.26 on Debian squeeze amd64),/dev/urandomOpenJDK和 Sun 都从. /dev/random出于某种原因,它们也都打开/dev/random了,但从不从中读取。因此,您阅读的博客文章要么是错误的,要么适用于与我的(显然是你的)不同的版本。

您可以检查您的是否读取/dev/random/dev/urandom跟踪它:

strace -o a.strace -f -e file java A

并查找跟踪的相关部分:

21165 open("/dev/random", O_RDONLY)     = 6
…
21165 open("/dev/urandom", O_RDONLY)    = 7
…
21165 read(7, "\322\223\211\262Zs\300\345\3264l\254\354[\6wS\326q@", 20) = 20
…

不用担心,/dev/urandom对于密码学来说非常好。

Java 的 SecureRandom确实使用 /dev/random,但只是短暂使用。

具体来说,它仅在生成种子信息时使用它,通过显式调用SecureRandom.generateSeed()或第一次调用nextInt()

因此,要重复您的 bash 示例,您可以执行以下操作,并且它应该阻塞。

import java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr;
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            sr = new SecureRandom();
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}

$JAVA_HOME/lib/security/java.security:securerandom.source属性指示使用时要使用的设备SecureRandom默认值为/dev/urandom,这意味着 Java 永远不会阻塞。

对于该主题的新手来说,/dev/urandom是 的特殊版本/dev/random,它将使用/dev/random可用时的真熵,然后使用伪随机数据播种真熵(如果可用)。

在无头、VM 和云环境中,我建议从外部来源播种 urandom,例如http://random.org

在最近的老式 Linux 上(比如说过去 6 年),/dev/random(阻塞)和 /dev/urandom(非阻塞)之间的计算差异非常小;原始熵通过随机数生成器清洗,两个设备使用相同的位。唯一的区别是 /dev/random 有一个速率限制器,可以防止它运行太久而不重新播种。利率估计本身是有争议的。

这在http://www.2uo.de/myths-about-urandom/中进行了解释,这使我们明白 /dev/urandom 足以为会话密钥的加密算法播种。如果您不经常调用它,那么两者应该具有相同或几乎相同的质量。

在运行需要同时建立 1000 多个 SSL 连接(会话密钥)的网络爬虫时,我看到通过 /dev/random 阻塞,是的,我们切换到 /dev/urandom;对于大量生成长期持有的公钥/私钥对(一种罕见的情况),自定义熵设备是最好的。