当前位置:   article > 正文

生成安全的随机数

生成随机数最安全的方法

阅读文本大概需要10分钟。

0x01:生产随机数的方式

  • Math.random()0到1之间随机数

  • java.util.Random伪随机数(线性同余法生成)

  • java.security.SecureRandom真随机数

  • java.util.concurrent.ThreadLocalRandom每一个线程有一个独立的随机数生成器

0x02:Math.random()

Math.random()产生的随机数是在0 到1之间的一个double类型的随机数,即 0 <= random <= 1

例子

  1. public static void main(String[] args) {
  2.         for(int i=0; i<10; i++){
  3.             System.out.println(Math.random());
  4.         }
  5.     }

结果

  1. 0.5561869175342243
  2. 0.8640888835150414
  3. 0.292814731626831
  4. 0.9873548780247475
  5. 0.32392504190968197
  6. 0.023230111447818613
  7. 0.7911879389548253
  8. 0.6453078158805755
  9. 0.8255443940742907
  10. 0.0476070934096563

看下图

从源码分析发现,调用Math.random()方法时,自动创建了一个伪随机数生成器,实际上用的是new java.util.Random()。

0x03:java.util.Random伪随机数

java.util.Random采用线性同余法伪随机数生成器(linear congruential pseudorandom number generator [简称LGC]),所以该随机数具有可预测性的缺点。在注重信息安全的应用中,不要使用 LCG 算法生成随机数,请使用 java.security.SecureRandom。

例子

  1. public static void main(String[] args) {
  2.         Random random = new Random();
  3.         for(int i=0; i<10; i++){
  4.             System.out.println(random.nextInt());
  5.         }
  6.     }

结果

  1. -932663432
  2. -1051420847
  3. -170246550
  4. 1965458676
  5. -2018336206
  6. 1399734206
  7. 1142954832
  8. -1274289135
  9. 918821980
  10. -537649235

java.util.Random类默认使用当前系统时钟作为种子

Random类提供的方法:

  • java.util.nextBoolean() - 返回均匀分布的 true 或者 false

  • nextBytes(byte[] bytes)

  • nextDouble() - 返回 0.0 到 1.0 之间的均匀分布的 double,Math.random() 调用的就是该方法

  • nextFloat() - 返回 0.0 到 1.0 之间的均匀分布的 float

  • nextGaussian() - 返回 0.0 到 1.0 之间的高斯分布(即正态分布)的 double

  • nextInt() - 返回均匀分布的 int

  • nextInt(int n) - 返回 0 到 n 之间的均匀分布的 int (包括 0,不包括 n)

  • nextLong() - 返回均匀分布的 long

  • setSeed(long seed) - 设置种子

另外只要种子一样,产生的随机数也一样; 因为种子确定,随机数算法也就确定了,所以输出是确定的。

例子

  1. public static void main(String[] args) {
  2.         Random random1 = new Random(1000);
  3.         Random random2 = new Random(1000);
  4.         for(int i=0; i<10; i++){
  5.             System.out.println("random1 = "+ random1.nextInt()
  6.             + ", random2 = "+ random2.nextInt());
  7.         }
  8.     }

结果

  1. random1 = -1244746321, random2 = -1244746321
  2. random1 = 1060493871, random2 = 1060493871
  3. random1 = -1826063944, random2 = -1826063944
  4. random1 = 1976922248, random2 = 1976922248
  5. random1 = -230127712, random2 = -230127712
  6. random1 = 68408698, random2 = 68408698
  7. random1 = 169247282, random2 = 169247282
  8. random1 = -735843605, random2 = -735843605
  9. random1 = 2089114528, random2 = 2089114528
  10. random1 = 1533708900, random2 = 1533708900

0x04:java.security.SecureRandom真随机数

java.Security.SecureRandom继承java.util.Random,操作系统收集了一些随机事件,比如鼠标点击、键盘点击等,java.Security.SecureRandom使用这些随机事件作为种子。

SecureRandom 提供加密的强随机数生成器(This class provides a cryptographically strong random number generator [RNG]),要求种子必须是不可预知的,产生非确定性输出。SecureRandom 也提供了与实现无关的算法,因此调用方(应用程序代码)会请求特定的 RNG 算法并将它传回到该算法的 SecureRandom 对象中。

  • 如果仅指定算法名称,如下所示:

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
  • 如果既指定了算法名称又指定了包提供程序,如下所示:

SecureRandom random = SecureRandom.getInstance("SHA1PRNG""SUN");

例子一

  1. public static void main(String[] args) {
  2.         SecureRandom random1 = new SecureRandom();
  3.         SecureRandom random2 = new SecureRandom();
  4.         for(int i=0; i<10; i++){
  5.             System.out.println("random1 = "+ random1.nextInt()
  6.             + ", random2 = "+ random2.nextInt());
  7.         }
  8.     }

结果

  1. random1 = -2132909245, random2 = 1721111490
  2. random1 = 1878066989, random2 = 331714565
  3. random1 = 570084968, random2 = 270567587
  4. random1 = -369271183, random2 = -2099748127
  5. random1 = -1588034927, random2 = 716208447
  6. random1 = 1272448595, random2 = -1076872105
  7. random1 = 651517544, random2 = -412298117
  8. random1 = 599063484, random2 = 990299359
  9. random1 = 1327006915, random2 = -1678337338
  10. random1 = 1555188183, random2 = -1062601998

例子二

  1. public static void main(String[] args) {
  2.         try {
  3.             SecureRandom random1 = SecureRandom.getInstance("SHA1PRNG");
  4.             SecureRandom random2 = SecureRandom.getInstance("SHA1PRNG");
  5.             for (int i = 0; i < 5; i++) {
  6.                 System.out.println(random1.nextInt() + " != " + random2.nextInt());
  7.             }
  8.         } catch (NoSuchAlgorithmException e) {
  9.             e.printStackTrace();
  10.         }
  11.     }

结果

  1. -65146125 != -293869330
  2. 602260319 != 236779048
  3. -313979165 != -450531309
  4. 1647234976 != 1324919270
  5. 1080689624 != 1189679018

0x05:java.util.concurrent.ThreadLocalRandom每一个线程有一个独立的随机数生成器

ThreadLocalRandom 是 JDK 7 之后提供的,继承 java.util.Random。每一个线程有一个独立的随机数生成器,用于并发产生随机数,能够解决多个线程发生的竞争争夺,效率更高。

ThreadLocalRandom 不是直接用 new 实例化,而是第一次使用其静态方法 current() 得到 ThreadLocal<ThreadLocalRandom> 实例,然后调用 java.util.Random 类提供的方法获得各种随机数。

例子

  1. public static void main(String[] args) {
  2.         for (int i = 0; i < 5; i++) {
  3.              System.out.println(ThreadLocalRandom.current().nextDouble());
  4.         }
  5.     }

结果

  1. 0.1203480287115829
  2. 0.5205724433165335
  3. 0.5543373628235388
  4. 0.3320090877996178
  5. 0.5277150915439681

往期精彩

01 漫谈发版哪些事,好课程推荐

02 Linux的常用最危险的命令

03 精讲Spring&nbsp;Boot—入门+进阶+实例

04 优秀的Java程序员必须了解的GC哪些

05 互联网支付系统整体架构详解

关注我

每天进步一点点

很干!在看吗?☟

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/413387
推荐阅读
相关标签
  

闽ICP备14008679号