Java随机数⽣成器RandomSecureRandom原理分析⽂章⽬录
Java ⾥提供了⼀些⽤于⽣成随机数的⼯具类,这⾥分析⼀下其实现原理,以及他们之间的区别、使⽤场景。java.util.Random
Random 是⽐较常⽤的随机数⽣成类,它的基本信息在类的注释⾥都写到了,下⾯是 JDK8 ⾥该类的注释:
/**
* An instance of this class is used to generate a stream of
* pseudorandom numbers. The class uses a 48-bit seed, which is
* modified using a linear congruential formula. (See Donald Knuth,
* <i>The Art of Computer Programming, Volume 2</i>, Section 3.2.1.)
* <p>
* If two instances of {@code Random} are created with the same
* seed, and the same sequence of method calls is made for each, they
* will generate and return identical sequences of numbers. In order to
* guarantee this property, particular algorithms are specified for the
* class {@code Random}. Java implementations must use all the algorithms
* shown here for the class {@code Random}, for the sake of absolute
* portability of Java code. However, subclasses of class {@code Random}
* are permitted to use other algorithms, so long as they adhere to the
* general contracts for all the methods.
* <p>
* The algorithms implemented by class {@code Random} use a
* {@code protected} utility method that on each invocation can supply
* up to 32 pseudorandomly generated bits.
* <p>
* Many applications will find the method {@link Math#random} simpler to use.
*
* <p>Instances of {@code java.util.Random} are threadsafe.
* However, the concurrent use of the same {@code java.util.Random}
* instance across threads may encounter contention and consequent
* poor performance. Consider instead using
* {@link urrent.ThreadLocalRandom} in multithreaded
* designs.
*
* <p>Instances of {@code java.util.Random} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom} to
* get a cryptographically secure pseudo-random number generator for use
* by security-sensitive applications.
*
* @author Frank Yellin
* @since 1.0
*/
翻译⼀下,主要有以下⼏点:
1. Random 类使⽤线性同余法 linear congruential formula 来⽣成伪随机数。
2. 两个 Random 实例,如果使⽤相同的种⼦ seed,那他们产⽣的随机数序列也是⼀样的。
3. Random 是线程安全的,你的程序如果对性能要求⽐较⾼的话,推荐使⽤ ThreadLocalRandom。
4. Random 不是密码学安全的,加密相关的推荐使⽤ SecureRandom。
Random 的基本⽤法如下所⽰:
Random random =new Random();
int r = Int();// ⽣成⼀个随机数
从下⾯的源码中可以看到,Random 的默认使⽤当前系统时钟来⽣成种⼦ seed。
private static final AtomicLong seedUniquifier =new AtomicLong(8682522807148012L);
public Random(){
this(seedUniquifier()^ System.nanoTime());
}
public Random(long seed){
if(getClass()== Random.class)
this.seed =new AtomicLong(initialScramble(seed));
else{
// subclass might have overriden setSeed
this.seed =new AtomicLong();
setSeed(seed);
}
}
private static long seedUniquifier(){
for(;;){
long current = ();
long next = current *181783497276652981L;
if(seedUniquifierpareAndSet(current, next))
return next;
}
}
java.Security.SecureRandom
介绍 Random 类时提到过,要⽣成加密基本的随机数应该使⽤ SecureRandom 类,该类信息如下所⽰:
/**
* This class provides a cryptographically strong random number
* generator (RNG).
*
* <p>A cryptographically strong random number
* minimally complies with the statistical random number generator tests
* specified in <a href="v/cryptval/140-2.htm">
* <i>FIPS 140-2, Security Requirements for Cryptographic Modules</i></a>,
* section 4.9.1.
* Additionally, SecureRandom must produce non-deterministic output.
* Therefore any seed material passed to a SecureRandom object must be
* unpredictable, and all SecureRandom output sequences must be
* cryptographically strong, as described in
* <a href="/">
* <i>RFC 1750: Randomness Recommendations for Security</i></a>.
*
* <p>A caller obtains a SecureRandom instance via the
* no-argument constructor or one of the {@code getInstance} methods:
*
* <pre>
* SecureRandom random = new SecureRandom();
* </pre>
*
* <p> Many SecureRandom implementations are in the form of a pseudo-random
* number generator (PRNG), which means they use a deterministic algorithm
* to produce a pseudo-random sequence from a true random seed.
* Other implementations may produce true random numbers,
* and yet others may use a combination of both techniques.
*
* <p> Typical callers of SecureRandom invoke the following methods
* to retrieve random bytes:
*
* <pre>
* SecureRandom random = new SecureRandom();
* byte bytes[] = new byte[20];
* Bytes(bytes);
* </pre>
*
* <p> Callers may also invoke the {@code generateSeed} method
* to generate a given number of seed bytes (to seed other random number
* generators, for example):
* <pre>
* byte seed[] = ateSeed(20);
* </pre>
*
* Note: Depending on the implementation, the {@code generateSeed} and
* {@code nextBytes} methods may block as entropy is being gathered,
* for example, if they need to read from /dev/random on various Unix-like
* operating systems.
*/
主要有以下⼏点:
1. 该类提供了能满⾜加密要求的强随机数⽣成器。
2. 传递给 SecureRandom 种⼦必须是不可预测的,seed 使⽤不当引发的安全漏洞可以看看 。
1. ⼀般使⽤默认的种⼦⽣成策略就⾏,对应 Linux ⾥⾯就是 /dev/random 和 /dev/urandom。其实现原理是:操作系统收集了
⼀些随机事件,⽐如⿏标点击,键盘点击等等,SecureRandom 使⽤这些随机事件作为种⼦。
2. 使⽤ /dev/random 来⽣成种⼦时,可能会因为熵不够⽽阻塞,性能⽐较差。
SecureRandom ⽤法如下所⽰:
SecureRandom random =new SecureRandom();
byte[] data = Bytes(16);
java生成随机数的方法下⾯我们看看其内部实现:
synchronized public void nextBytes(byte[] bytes){
}
public SecureRandom(){
super(0);
getDefaultPRNG(false, null);
}
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 = SunProvider();
if(setSeed){
ineSetSeed(seed);
}
}else{
try{
SecureRandom random = Instance(prng);
this.secureRandomSpi = SecureRandomSpi();
this.provider = Provider();
if(setSeed){
ineSetSeed(seed);
}
}catch(NoSuchAlgorithmException nsae){
// never happens, because we made sure the algorithm exists
throw new RuntimeException(nsae);
}
}
if(getClass()== SecureRandom.class){
this.algorithm = prng;
}
}
在 mac 环境下使⽤ JDK8 测试时发现,默认使⽤了 NativePRNG ⽽⾮ SHA1PRNG,但是 NativePRNG 其实还是在
sun.security.provider.SecureRandom 的基础上做了⼀些封装。
在 sun.security.provider.SeedGenerator 类⾥,可以看到 seed 是利⽤ /dev/random 或 /dev/urandom 来⽣成的,启动应⽤程序时可以通过参数 -d=file:/dev/urandom 来指定 seed 源。
static{
String var0 = SeedSource();
if(!var0.equals("file:/dev/random")&&!var0.equals("file:/dev/urandom")){
if(var0.length()!=0){
try{
instance =new SeedGenerator.URLSeedGenerator(var0);
if(debug != null){
debug.println("Using URL seed generator reading from "+ var0);
}
}catch(IOException var2){
if(debug != null){
debug.println("Failed to create seed generator with "+ var0 +": "+ String());
}
}
}
}else{
try{
instance =new NativeSeedGenerator(var0);
if(debug != null){
debug.println("Using operating system seed generator"+ var0);
}
}catch(IOException var3){
if(debug != null){
debug.println("Failed to use operating system seed generator: "+ String());
}
}
}
if(instance == null){
if(debug != null){
debug.println("Using default threaded seed generator");
}
instance =new SeedGenerator.ThreadedSeedGenerator();
}
}
在 Random 类⾥,多个实例设置相同的seed,产⽣的随机数序列也是⼀样的。⽽ SecureRandom 则不同,运⾏下⾯的代码:
public class RandomTest {
public static void main(String[] args){
byte[] seed ="hello".getBytes();
for(int i =0; i <10;++i){
SecureRandom secureRandom =new SecureRandom(seed);
System.out.Int());
}
}
}
输出如下所⽰,每次运⾏产⽣的随机数都不⼀样。
-2105877601
1151182748
1329080810
-617594950
2094315881
-1649759687
-1360561033
-653424535
-927058354
-1577199965
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论