Java SecureRandom的一点记录

本文探讨了Java中SecureRandom类在Linux环境下可能导致线程阻塞的问题。具体介绍了SecureRandom的工作原理,不同操作系统下的默认算法,以及如何通过配置避免性能瓶颈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先记录一下两个帖子的地址:
1. [url=https://www.cigital.com/blog/proper-use-of-javas-securerandom/]Proper Use Of Java SecureRandom[/url]
2. [url=https://tersesystems.com/2015/12/17/the-right-way-to-use-securerandom/]The Right Way to Use SecureRandom[/url]

JDK版本: 1.8.0_66

事情的起因还是 SecureRandom.generateSeed()在Linux系统上卡住导致线程block这个现象。见讨论:
http://stackoverflow.com/questions/137212/how-to-solve-performance-problem-with-java-securerandom

事实上,如果使用SecureRandom.getInstanceStrong()这种方法初始化SecureRandom对象的话,也会在Linux上产生类似问题。

记录一下知识点(这玩意儿其实挺冷门的):
1.SecureRandom本身并不是伪随机算法的实现,而是使用了其他类提供的算法来获取伪随机数。

2.如果简单的new一个SecureRandom对象的话,在不同的操作平台会获取到不同的算法,windows默认是SHA1PRNG,Linux的话是NativePRNG。

3. Linux下的NativePRNG,如果调用generateSeed()方法,这个方法会读取Linux系统的/dev/random文件,这个文件在JAVA_HOME/jre/lib/securiy/java.security里面有默认定义。而/dev/random文件是动态生成的,如果没有数据,就会阻塞(后面这句结论阻塞正确,至于为什么阻塞没细看)。也就造成了第一个现象。

4.可以使用-Djava.security.egd=file:/dev/./urandom (这个文件名多个u)强制使用/dev/urandom这个文件,避免阻塞现象。中间加个点的解释是因为某个JDK BUG,SO那个帖子有链接。

5.如果使用SecureRandom.getInstanceStrong()这种方法初始化SecureRandom对象的话,会使用NativePRNGBlocking这个算法,而NativePRNGBlocking算法的特性如下:
NativePRNGBlocking uses /dev/random for all of the following operations:
Initial seeding: This initializes an internal SHA1PRNG instance using 20 bytes from /dev/random
Calls to nextBytes(), nextInt(), etc.: This provides the XOR of the output from the internal SHA1PRNG instance (see above) and data read from /dev/random
Calls to getSeed(): This provides data read from /dev/random

可见这个算法完全依赖/dev/random,所以当这个文件随机数不够的时候,自然会导致卡顿了。
6.如果使用NativePRNGBlocking算法的话,4中的系统参数失效!!!(这个是从http://hongjiang.info/java8-nativeprng-blocking/看到的)
7.一般使用SecureRandom不需要设置Seed,不需要设置算法,使用默认的,甚至一个静态的即可,如果有需求的话可以在运行一段时间后setSeed一下。(这点是自己总结的)
### Java 中 `SecureRandom` 类的用法 #### 创建 `SecureRandom` 实例 为了创建一个安全随机数生成器实例,可以使用默认构造函数: ```java import java.security.SecureRandom; SecureRandom secureRandom = new SecureRandom(); ``` 此方法会初始化一个新的伪随机数生成器 (PRNG),它能够提供加密强度的随机值[^1]。 如果希望指定特定算法或提供商,则可以通过名称获取其实现: ```java // 使用 SHA1PRNG 算法 SecureRandom shaPrng = SecureRandom.getInstance("SHA1PRNG"); // 或者指明服务供应商 SecureRandom bcInstance = SecureRandom.getInstance("NativePRNG", "SUN"); ``` #### 获取随机字节序列 一旦有了 `SecureRandom` 对象,就可以调用其方法来获得随机数据。最常用的方法之一是从给定数组填充随机字节: ```java byte[] bytes = new byte[8]; secureRandom.nextBytes(bytes); System.out.println(java.util.Arrays.toString(bytes)); ``` 这段代码将打印出由八个随机整数值组成的列表[^2]。 对于更复杂的场景,比如当应用程序启动时可能无法立即得到足够的熵源,可以在创建对象之后显式地混合一些额外输入进去: ```java String seedMaterial = System.currentTimeMillis() + "-" + UUID.randomUUID().toString(); secureRandom.setSeed(seedMaterial.getBytes(StandardCharsets.UTF_8)); // 接着再读取一次以确保种子被充分混入内部状态中 secureRandom.nextInt(); ``` 这种方法有助于提高安全性并减少可预测性风险[^3]。 #### 解决常见问题 有时,在某些环境中可能会遇到性能瓶颈或者长时间等待的情况。这通常是因为 JVM 正试图收集足够多的信息作为初始种子材料。一种解决方案是在配置文件 `${JAVA_HOME}/jre/lib/security/java.security` 中调整参数设置,例如更改 PRNG 的实现方式为非阻塞版本: 修改如下所示: ``` securerandom.source=file:/dev/urandom ``` 上述改动使得即使在资源受限条件下也能快速响应请求而不必担心住的问题[^4]。 #### 完整示例程序 下面给出了一段完整的例子,展示了如何利用 `ThreadLocalRandom` `SecureRandom` 来生成不同类型的随机数: ```java public class RandomExample { public static void main(String[] args) throws Exception { // 使用 ThreadLocalRandom 生成普通用途的随机数 int threadLocalInt = ThreadLocalRandom.current().nextInt(); // 初始化 SecureRandom 并生成强随机数 try (final var sr = SecureRandom.getInstanceStrong()) { final long strongLong = sr.nextLong(); System.out.printf("ThreadLocalRandom integer: %d%nSecureRandom long value: %d%n", threadLocalInt, strongLong); } } } ``` 该片段不仅体现了两种不同的随机数生产策略之间的差异,还强调了何时应该优先考虑更强的安全保障措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值