*使用不当的方式*
byte[] salt = new byte[128];
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(System.currentTimeMillis()); //使用系统时间作为种子
secureRandom.nextBytes(salt);
为什么?
此处指定了当前系统时间作为种子,替代系统默认随机源。如果同一毫秒连续调用,则得到的随机数则是相同的。
小结:不要自己指定种子。应当使用系统随机源。
系统默认的随机源是什么?
这取决于$JAVA_HOME/jre/lib/security/java.security配置中的securerandom.source属性。例如jdk1.8中该配置为:
securerandom.source=file:/dev/random
SecureRandom漏洞触发条件
在调用SecureRandom类的构造函数SecureRandom(byte[] seed)。 或者在生成随机数之前调用setSeed(long seed)或者setSeed(byte[] seed)方法设置随机种子
nextInt() | 从Random继承而来(同样的使用方式) |
nextBytes() | 生成用户指定的随机字节数。(覆盖Random中的nextBytes() ) |
SecureRandom——依然是伪随机;注意是收集了一些随机事件,比如鼠标点击,键盘点击等等,SecureRandom 使用这些随机事件作为种子。这意味着,种子是不可预测的,而不像 Random 默认使用系统当前时间的毫秒数作为种子,有规律可寻。
SecureRandom 内置两种随机数算法,NativePRNG 和 SHA1PRNG。
源码:
private void getDefaultPRNG(boolean setSeed, byte[] seed) {
String prng = getPrngAlgorithm();
if (prng == null) {
// bummer, get the SUN implementation
prng = "SHA1PRNG";
this.secureRandomSpi = new sun.security.provider.SecureRandom();
this.provider = new sun.security.provider.Sun();
if (setSeed) {
this.secureRandomSpi.engineSetSeed(seed);
}
} else {
try {
SecureRandom random = SecureRandom.getInstance(prng);
this.secureRandomSpi = random.getSecureRandomSpi();
this.provider = random.getProvider();
if (setSeed) {
this.secureRandomSpi.engineSetSeed(seed);
}
} catch (NoSuchAlgorithmException nsae) {
// never happens, because we made sure the algorithm exists
throw new RuntimeException(nsae);
}
}
获取实例的方式
1.在使用new的方式获得实例时,默认是SHA1PRNG算法(不单独配置的情况下)
2.修改securerandom.source=file:/dev/random
或者 securerandom.source=file:/dev/urandom
两者之一就是NativePRNG
,否则就是SHA1PRNG
3.
通过getInstance
来初始化对象,有一个参数的,直接传一个算法名就行,如果不存在算法抛异常;另外有两个参数的,第二个参数还可以指定算法程序包
SecureRandom secureRandom = new SecureRandom();
SecureRandom secureRandom3 = SecureRandom.getInstance("SHA1PRNG");
SecureRandom secureRandom2 = SecureRandom.getInstance("SHA1PRNG", "SUN");
算法区别:
——SHA1PRNG 性能好,NativePRNG 安全性高。