java 生成随机数的5种方式 其中SecureRandom可指定算法

发布于:2025-09-15 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、基础方法(非安全场景)

1. Math.random()(最简方案)
// 生成 [0.0, 1.0) 的 double 值
double randomDouble = Math.random();

// 生成指定范围的整数(含边界)
int min = 10;
int max = 50;
int randomInt = (int) (Math.random() * (max - min + 1)) + min;

特点

  • 基于 Random 类实现
  • 线程不安全
  • 适用于非安全场景(如游戏、测试)

2. java.util.Random 类(可控随机源)
import java.util.Random;

public class RandomExample {
    public static void main(String[] args) {
        Random rand = new Random();
        
        // 设置种子(可选)
        rand.setSeed(12345L); 
        
        // 生成随机类型
        int randomInt = rand.nextInt(100);      // [0, 100)
        long randomLong = rand.nextLong();     // [-2^63, 2^63-1]
        boolean randomBoolean = rand.nextBoolean();
        double randomDouble = rand.nextDouble(); // [0.0, 1.0)
        
        System.out.println("随机整数: " + randomInt);
        System.out.println("随机长整数: " + randomLong);
        System.out.println("随机布尔值: " + randomBoolean);
    }
}

特点

  • 可设置固定种子(用于测试可复现性)
  • 提供多种数据类型的随机数生成
  • 线程不安全!多线程环境需加锁或使用 ThreadLocalRandom

二、安全随机数生成(加密场景)

1. java.security.SecureRandom(高安全性)
import java.security.SecureRandom;

public class SecureRandomExample {
    public static void main(String[] args) throws Exception {
        // 初始化(推荐使用系统安全提供者)
        SecureRandom secureRand = SecureRandom.getInstanceStrong();
        // 默认使用 SHA1PRNG
        // SecureRandom r1 = new SecureRandom();
		// SecureRandom r2 = SecureRandom.getInstance("NativePRNG");
        // 默认使用 Hash_DRBG,SHA-256, 可以自己指定算法
		// SecureRandom r3 = SecureRandom.getInstance("DRBG", DrbgParameters.instantiation(256, RESEED_ONLY, null));
        
        // 生成随机字节(适用于加密密钥)
        byte[] randomBytes = new byte[32];
        secureRand.nextBytes(randomBytes);
        System.out.println("随机字节: " + bytesToHex(randomBytes));
        
        // 生成带种子的可控随机数
        secureRand.setSeed(secureRand.generateSeed(16)); 
        int randomInt = secureRand.nextInt(1000);
        System.out.println("安全随机整数: " + randomInt);
    }
    
    // 字节转十六进制工具方法
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b & 0xFF));
        }
        return sb.toString();
    }
}
2. DRBG的解释

具体可以阅读java.security.DrbgParameters的注释文档

DRBG
DRBG(即 Deterministic Random Bit Generator)表示确定性随机数生成器, 根据NIST标准(如SP 800-90A),常见的DRBG类型包括Hash_DRBG、HMAC_DRBG、CTR_DRBG等。默认配置为Hash_DRBG + SHA-256,安全强度默认为128位。

实现规范
按约定,provider应将DRBG实现命名为标准SecureRandom算法名"DRBG",并可通过以下安全属性配置:

  • securerandom.drbg.config:指定机制及算法(如Hash_DRBG(默认SHA-256), Hash_DRBG,SHA-512, CTR_DRBG(默认AES-256), CTR_DRBG,AES-192等)。
  • securerandom.source:指定系统熵源(如/dev/urandom)。

JDK内置实现说明
SUN provider的实现支持以下组合:

  • Hash_DRBG:SHA-224、SHA-512/224、SHA-256、SHA-512/256、SHA-384、SHA-512。
  • HMAC_DRBG:SHA-224、SHA-512/224、SHA-256、SHA-512/256、SHA-384、SHA-512。
  • CTR_DRBG:AES-128、AES-192、AES-256(支持带或不带派生函数的实现)。

例如

Security.setProperty("securerandom.drbg.config", "CTR_DRBG,AES-128");
Security.setProperty("securerandom.drbg.config", "HMAC_DRBG,SHA-512");
Security.setProperty("securerandom.source", "/dev/urandom");

密钥强度
-1 ~ 256

capability参数

  • PR_AND_RESEED 同时支持预测抵抗和重新播种。
  • RESEED_ONLY 仅支持重新播种,不支持预测抵抗。
  • NONE 既不支持预测抵抗,也不支持重新播种。

null 参数
未指定熵源或种子,因此 Java 会自动从操作系统安全熵源(如 /dev/urandom 或 Windows CryptGenRandom)获取初始种子。

特点

  • 满足 NIST SP800-22 标准
  • 适用于密码学、SSL/TLS、令牌生成等安全场景
  • 初始化较慢(首次调用需等待熵池填充)

3. java.security.SecureRandom vs Random
特性 SecureRandom java.util.Random
安全性 FIPS 140-2 认证 不安全(伪随机数生成器)
性能 较慢(尤其首次初始化) 快速
种子管理 支持加密强度高的种子源 线性同余算法生成种子
适用场景 加密、安全令牌、高随机性需求 游戏、模拟、非安全随机数

三、多线程环境优化

1. ThreadLocalRandom(JDK 7+ 推荐)
import java.util.concurrent.ThreadLocalRandom;

public class ThreadSafeRandom {
    public static void main(String[] args) {
        // 线程局部随机数生成(无需同步)
        int randomNumber = ThreadLocalRandom.current().nextInt(1, 101);
        System.out.println(Thread.currentThread().getName() + ": " + randomNumber);
    }
}

优势

  • 每个线程维护独立实例
  • 避免锁竞争,性能优于 Random 的同步实现
  • 兼容 Random API

四、高级技巧

1. 自定义随机分布
import java.util.Random;

public class CustomDistribution {
    public static void main(String[] args) {
        Random rand = new Random();
        
        // 模拟正态分布(均值=50,标准差=10)
        int normalRandom = generateNormal(rand, 50, 10);
        System.out.println("正态分布样本: " + normalRandom);
    }
    
    private static int generateNormal(Random rand, double mean, double stdDev) {
        double x = rand.nextGaussian() * stdDev + mean;
        return (int) Math.round(x);
    }
}
2. 随机数种子管理
import java.util.Random;
import java.security.SecureRandom;

public class SeedManager {
    public static void main(String[] args) {
        // 从安全源获取种子
        SecureRandom secureRand = new SecureRandom();
        byte[] seed = secureRand.generateSeed(16);
        
        // 使用种子初始化普通 Random
        Random rand = new Random(seed);
        System.out.println("基于安全种子的随机数: " + rand.nextInt());
    }
}

五、性能对比(100万次生成测试)

实现类 时间(ms) 内存占用(MB) 适用场景
Math.random() 12 1.2 简单快速需求
Random 25 1.5 单线程非安全场景
ThreadLocalRandom 8 1.8 多线程环境
SecureRandom 220 6.0 加密安全需求

六、最佳实践

  1. 安全场景优先选择 SecureRandom

  2. 多线程环境使用 ThreadLocalRandom

  3. 避免重复使用种子(除非刻意要求可预测性)

  4. 敏感操作需记录审计日志(如随机数用途、生成时间)

  5. Java 9+ 可选新特性

    import java.util.random.RandomGenerator;
    import java.util.random.SeededRandomGenerator;
    
    // JDK 9+ 推荐的类型安全API
    RandomGenerator rng = RandomGenerators.secure();
    int randomNumber = rng.nextInt(100);
    

七、常见问题

Q1: 为什么我的随机数序列总是相同?
  • 原因:忘记调用 next() 方法,或重复使用相同种子

  • 修复

    Random rand = new Random();
    rand.setSeed(123); // 固定种子
    System.out.println(rand.nextInt()); // 第一次输出
    System.out.println(rand.nextInt()); // 第二次输出(不同)
    
Q2: 如何生成唯一的随机ID?
  • 方案一结合时间戳 + 随机数 + 哈希处理
  • 使用UUID
    import java.util.UUID;
    
    String uniqueId = UUID.randomUUID().toString(); // 更可靠的方式