使用 ThreadLocalRandom 和 SecureRandom 随机生成盐

本文将介绍怎样使用 ThreadLocalRandom 和 SecureRandom 类随机生成盐,然后利用随机生成的盐调用 Crypt.crypt(key, salt) 方法计算消息摘要。

使用 ThreadLocalRandom 生成随机盐

与当前线程隔离的随机数生成器。就像 Math 类使用的全局随机生成器一样,ThreadLocalRandoman 是用内部生成的种子初始化的,否则生成的随机值可能不会被修改。在适用的情况下,在并发程序中使用线程随机对象而不是共享随机对象通常会减少开销和竞争。当多个任务(例如,每个 ForkJoinTask)在线程池中并行使用随机数时,使用 ThreadLocalRandom 特别合适。

这个类的用法通常应该是以下形式: 

ThreadLocalRandom.current().nextX(...)

其中:X 为 int、long 等。

当所有生成随机数的地方都使用这种形式时,就不可能在多个线程之间意外地共享 ThreadLocalRandom。

该类还提供了其他常用的有界随机生成方法。

实例代码

简单的使用 ThreadLocalRandom 生成随机数据,代码如下:

package com.huangx.codec.salt;

import java.util.concurrent.ThreadLocalRandom;

public class SaltDemo {

    public static void main(String[] args) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        System.out.println("nextBoolean:" + threadLocalRandom.nextBoolean());
        System.out.println("nextFloat:" + threadLocalRandom.nextFloat());
        System.out.println("nextDouble:" + threadLocalRandom.nextDouble());
        System.out.println("nextInt:" + threadLocalRandom.nextInt());
        System.out.println("nextLong:" + threadLocalRandom.nextLong());

        // 通过链式调用,避免线程间共享
        // 生成一个 0 ~ 100 之间的随机数
        System.out.println(ThreadLocalRandom.current().nextInt(100));
    }

}

运行实例代码,输出结果如下:

nextBoolean:true
nextFloat:0.1397484
nextDouble:0.47617774122967327
nextInt:451790857
nextLong:-3611781014752768478
48

使用 SecureRandom 生成随机盐

这个类提供了一个加密的强随机数生成器(RNG)。

强加密随机数至少要遵从 FIPS 140-2, Security Requirements for Cryptographic Modules 中 4.9.1 部分指定的统计随机数生成器测试。此外,SecureRandom 还必须生成非确定性输出。因此,根据 RFC 1750: Randomness Recommendations for Security 中的描述,任何传递给 SecureRandom 对象的种子材料必须是不可预知的,所有 SecureRandom 的输出序列必须是强加密的。

调用者可通过无参数构造方法或一个 getInstance 方法获取 SecureRandom 实例:

 SecureRandom random = new SecureRandom();

许多 SecureRandom 实现是伪随机数生成器 (PRNG) 的形式,这意味着它们将使用确定的算法根据实际随机种子生成伪随机序列。其他实现可以生成实际随机数,而另一些实现则可能结合使用这两项技术。

通常,SecureRandom 的调用者调用以下方法获取随机字节:

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);

调用者还可以调用 generateSeed 方法生成给定的种子字节数(例如,为其他随机数生成器提供种子):

byte seed[] = random.generateSeed(20);

SecureRandom 在 Crypt 中的应用

查看 Apache Commons Codec 的 Crypt 类的源码,其中 crypt(final byte[] keyBytes, final String salt) 方法源码如下:

// Crypt.java
public static String crypt(final byte[] keyBytes, final String salt) {
    if (salt == null) {
        return Sha2Crypt.sha512Crypt(keyBytes);
    } else if (salt.startsWith(Sha2Crypt.SHA512_PREFIX)) {
        return Sha2Crypt.sha512Crypt(keyBytes, salt);
    } else if (salt.startsWith(Sha2Crypt.SHA256_PREFIX)) {
        return Sha2Crypt.sha256Crypt(keyBytes, salt);
    } else if (salt.startsWith(Md5Crypt.MD5_PREFIX)) {
        return Md5Crypt.md5Crypt(keyBytes, salt);
    } else {
        return UnixCrypt.crypt(keyBytes, salt);
    }
}

上面 crypt 方法中,当盐(salt)为 null 时,将直接调用 Sha2Crypt.sha512Crypt() 方法

// Sha2Crypt.java
public static String sha512Crypt(final byte[] keyBytes) {
    return sha512Crypt(keyBytes, null);
}

public static String sha512Crypt(final byte[] keyBytes, String salt) {
    // 如果当盐(salt)为 null 时,则使用 B64.getRandomSalt(8) 生成一个 8 位数的随机盐
    if (salt == null) {
        salt = SHA512_PREFIX + B64.getRandomSalt(8);
    }
    return sha2Crypt(keyBytes, salt, SHA512_PREFIX, SHA512_BLOCKSIZE, MessageDigestAlgorithms.SHA_512);
}

上面的 sha512Crypt(final byte[] keyBytes, String salt) 方法将使用 B64.getRandomSalt(8) 八位的随机字符串,盐则等于“SHA512前缀 + 八位随机数”。接下来我们看看 B64.java 中的 getRandomSalt 方法,如下:

// B64.java 
static String getRandomSalt(final int num) {
  return getRandomSalt(num, new SecureRandom());
}

static String getRandomSalt(final int num, final Random random) {
  final StringBuilder saltString = new StringBuilder(num);
  for (int i = 1; i <= num; i++) {
    saltString.append(B64T_STRING.charAt(random.nextInt(B64T_STRING.length())));
  }
  return saltString.toString();
}

上面的 getRandomSalt(final int num) 方法中,使用 SecureRandom 类作为生成随机数的工具。

示例代码

下面实例将生成一个长度为 12 的随机字符串,该字符串只包含 A-Z、a-z 和 0-9。实现代码如下:

package com.huangx.codec.crypt;

import java.security.SecureRandom;

public class CryptDemo4 {
    private static final String ORGIN_STR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        SecureRandom secureRandom = new SecureRandom();
        for(int i = 0; i < 12; i++) {
            builder.append(ORGIN_STR.charAt(secureRandom.nextInt(ORGIN_STR.length())));
        }
        System.out.println("随机字符串:" + builder.toString());
    }

}

运行示例代码,输出结果如下:

随机字符串:07vvz04A6sQa
越是没有本领的就越加自命不凡。——邓拓
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号