randomness

更新时间:2023-01-04 17:57:25 阅读: 评论:0


2023年1月4日发(作者:汽车后桥)

Java随机数⽣成器RandomSecureRandom原理分析

⽂章⽬录

Java⾥提供了⼀些⽤于⽣成随机数的⼯具类,这⾥分析⼀下其实现原理,以及他们之间的区别、使⽤场景。

Random是⽐较常⽤的随机数⽣成类,它的基本信息在类的注释⾥都写到了,下⾯是JDK8⾥该类的注释:

/**

*Aninstanceofthisclassisudtogenerateastreamof

*ssusa48-bited,whichis

*modifiedusingalinearcongruentialformula.(SeeDonaldKnuth,

*TheArtofComputerProgramming,Volume2,Section3.2.1.)

*

*Iftwoinstancesof{@codeRandom}arecreatedwiththesame

*ed,andthesamequenceofmethodcallsismadeforeach,they

*rto

*guaranteethisproperty,particularalgorithmsarespecifiedforthe

*class{@codeRandom}.Javaimplementationsmustuallthealgorithms

*shownherefortheclass{@codeRandom},forthesakeofabsolute

*r,subclassofclass{@codeRandom}

*arepermittedtouotheralgorithms,solongastheyadheretothe

*generalcontractsforallthemethods.

*

*Thealgorithmsimplementedbyclass{@codeRandom}ua

*{@codeprotected}utilitymethodthatoneachinvocationcansupply

*upto32pudorandomlygeneratedbits.

*

*Manyapplicationswillfindthemethod{@linkMath#random}simplertou.

*

*

Instancesof{@}arethreadsafe.

*However,theconcurrentuofthesame{@}

*instanceacrossthreadsmayencountercontentionandconquent

*erinsteadusing

*{@LocalRandom}inmultithreaded

*designs.

*

*

Instancesof{@}arenotcryptographically

*erinsteadusing{@Random}to

*getacryptographicallycurepudo-randomnumbergeneratorforu

*bycurity-nsitiveapplications.

*

*@authorFrankYellin

*@since1.0

*/

翻译⼀下,主要有以下⼏点:

类使⽤线性同余法linearcongruentialformula来⽣成伪随机数。

2.两个Random实例,如果使⽤相同的种⼦ed,那他们产⽣的随机数序列也是⼀样的。

是线程安全的,你的程序如果对性能要求⽐较⾼的话,推荐使⽤ThreadLocalRandom。

不是密码学安全的,加密相关的推荐使⽤SecureRandom。

Random的基本⽤法如下所⽰:

Randomrandom=newRandom();

intr=t();//⽣成⼀个随机数

从下⾯的源码中可以看到,Random的默认使⽤当前系统时钟来⽣成种⼦ed。

privatestaticfinalAtomicLongedUniquifier=newAtomicLong(8682522807148012L);

publicRandom(){

this(edUniquifier()^me());

}

publicRandom(longed){

if(getClass()==)

=newAtomicLong(initialScramble(ed));

el{

//subclassmighthaveoverridentSeed

=newAtomicLong();

tSeed(ed);

}

}

privatestaticlongedUniquifier(){

for(;;){

longcurrent=();

longnext=current*6652981L;

if(eAndSet(current,next))

returnnext;

}

}

Random

介绍Random类时提到过,要⽣成加密基本的随机数应该使⽤SecureRandom类,该类信息如下所⽰:

/**

*Thisclassprovidesacryptographicallystrongrandomnumber

*generator(RNG).

*

*

Acryptographicallystrongrandomnumber

*minimallycomplieswiththestatisticalrandomnumbergeneratortests

*specifiedin

*FIPS140-2,SecurityRequirementsforCryptographicModules,

*ction4.9.1.

*Additionally,SecureRandommustproducenon-deterministicoutput.

*ThereforeanyedmaterialpasdtoaSecureRandomobjectmustbe

*unpredictable,andallSecureRandomoutputquencesmustbe

*cryptographicallystrong,asdescribedin

*

*RFC1750:RandomnessRecommendationsforSecurity.

*

*

AcallerobtainsaSecureRandominstanceviathe

*no-argumentconstructororoneofthe{@codegetInstance}methods:

*

*

*SecureRandomrandom=newSecureRandom();

*

*

*

ManySecureRandomimplementationsareintheformofapudo-random

*numbergenerator(PRNG),whichmeanstheyuadeterministicalgorithm

*toproduceapudo-randomquencefromatruerandomed.

*Otherimplementationsmayproducetruerandomnumbers,

*andyetothersmayuacombinationofbothtechniques.

*

*

TypicalcallersofSecureRandominvokethefollowingmethods

*toretrieverandombytes:

*

*

*SecureRandomrandom=newSecureRandom();

*bytebytes[]=newbyte[20];

*tes(bytes);

*

*

*

Callersmayalsoinvokethe{@codegenerateSeed}method

*togenerateagivennumberofedbytes(toedotherrandomnumber

*generators,forexample):

*

*byteed[]=teSeed(20);

*

*

*Note:Dependingontheimplementation,the{@codegenerateSeed}and

*{@codenextBytes}methodsmayblockantropyisbeinggathered,

*forexample,iftheyneedtoreadfrom/dev/randomonvariousUnix-like

*operatingsystems.

*/

主要有以下⼏点:

1.该类提供了能满⾜加密要求的强随机数⽣成器。

2.传递给SecureRandom种⼦必须是不可预测的,ed使⽤不当引发的安全漏洞可以看看。

1.⼀般使⽤默认的种⼦⽣成策略就⾏,对应Linux⾥⾯就是/dev/random和/dev/urandom。其实现原理是:操作系统收集了

⼀些随机事件,⽐如⿏标点击,键盘点击等等,SecureRandom使⽤这些随机事件作为种⼦。

2.使⽤/dev/random来⽣成种⼦时,可能会因为熵不够⽽阻塞,性能⽐较差。

SecureRandom⽤法如下所⽰:

SecureRandomrandom=newSecureRandom();

byte[]data=tes(16);

下⾯我们看看其内部实现:

synchronizedpublicvoidnextBytes(byte[]bytes){

NextBytes(bytes);

}

publicSecureRandom(){

super(0);

getDefaultPRNG(fal,null);

}

privatevoidgetDefaultPRNG(booleantSeed,byte[]ed){

Stringprng=getPrngAlgorithm();

if(prng==null){

//bummer,gettheSUNimplementation

prng="SHA1PRNG";

RandomSpi=Random();

er=Provider();

if(tSeed){

SetSeed(ed);

}

}el{

try{

SecureRandomrandom=tance(prng);

RandomSpi=ureRandomSpi();

er=vider();

if(tSeed){

SetSeed(ed);

}

}catch(NoSuchAlgorithmExceptionnsae){

//neverhappens,becauwemadesurethealgorithmexists

thrownewRuntimeException(nsae);

}

}

if(getClass()==){

thm=prng;

}

}

在mac环境下使⽤JDK8测试时发现,默认使⽤了NativePRNG⽽⾮SHA1PRNG,但是NativePRNG其实还是在

Random的基础上做了⼀些封装。

在nerator类⾥,可以看到ed是利⽤/dev/random或/dev/urandom来⽣成的,启动应⽤程序时

可以通过参数-=file:/dev/urandom来指定ed源。

static{

Stringvar0=dSource();

if(!("file:/dev/random")&&!("file:/dev/urandom")){

if(()!=0){

try{

instance=dGenerator(var0);

if(debug!=null){

n("UsingURLedgeneratorreadingfrom"+var0);

}

}catch(IOExceptionvar2){

if(debug!=null){

n("Failedtocreateedgeneratorwith"+var0+":"+ng());

}

}

}

}el{

try{

instance=newNativeSeedGenerator(var0);

if(debug!=null){

n("Usingoperatingsystemedgenerator"+var0);

}

}catch(IOExceptionvar3){

if(debug!=null){

n("Failedtouoperatingsystemedgenerator:"+ng());

}

}

}

if(instance==null){

if(debug!=null){

n("Usingdefaultthreadededgenerator");

}

instance=edSeedGenerator();

}

}

在Random类⾥,多个实例设置相同的ed,产⽣的随机数序列也是⼀样的。⽽SecureRandom则不同,运⾏下⾯的代码:

publicclassRandomTest{

publicstaticvoidmain(String[]args){

byte[]ed="hello".getBytes();

for(inti=0;i<10;++i){

SecureRandomcureRandom=newSecureRandom(ed);

n(t());

}

}

}

输出如下所⽰,每次运⾏产⽣的随机数都不⼀样。

-2105877601

1151182748

1329080810

-617594950

2094315881

-1649759687

-1360561033

-653424535

-927058354

-1577199965

为什么呢?因为engineSetSeed⽅法设置ed时调⽤的是静态实例INSTANCE的implSetSeed⽅法,该⽅法通过

getMixedRandom得到的SecureRandom来设置ed,⽽这个SecureRandom初始化种⼦是系统的。

IOINSTANCE;

//inNativePRNG

protectedvoidengineSetSeed(byte[]var1){

tSeed(var1);

}

privatevoidimplSetSeed(byte[]var1){

Objectvar2=_SET_SEED;

synchronized(_SET_SEED){

if(!tInitialized){

tInitialized=true;

t=(OutputStream)ileged(newPrivilegedAction(){

publicOutputStreamrun(){

try{

returnnewFileOutputStream(le,true);

}catch(Exceptionvar2){

returnnull;

}

}

});

}

if(t!=null){

try{

(var1);

}catch(IOExceptionvar5){

thrownewProviderException("tSeed()failed",var5);

}

}

Random().engineSetSeed(var1);

}

}

privateSecureRandomgetMixRandom(){

SecureRandomvar1=dom;

if(var1==null){

Objectvar2=_GET_BYTES;

synchronized(_GET_BYTES){

var1=dom;

if(var1==null){

var1=newSecureRandom();

try{

byte[]var3=newbyte[20];

readFully(,var3);

SetSeed(var3);

}catch(IOExceptionvar5){

thrownewProviderException("initfailed",var5);

}

dom=var1;

}

}

}

returnvar1;

}

在SetSeed⽅法⾥,新种⼦的⽣成不仅和刚设置的ed有关,也和原来的种⼦(系统

产⽣的ed)有关。

//Random

publicsynchronizedvoidengineSetSeed(byte[]var1){

if(!=null){

();

for(intvar2=0;var2<;++var2){

[var2]=0;

}

}

=(var1);

}

/dev/random与/dev/urandom

在Linux操作系统中,有⼀个特殊的设备⽂件/dev/random,可以⽤作随机数发⽣器或伪随机数发⽣器。

在读取时,/dev/random设备会返回⼩于熵池噪声总数的随机字节。/dev/random可⽣成⾼随机性的公钥或⼀次性密码本。若熵池空

了,对/dev/random的读操作将会被阻塞,直到从别的设备中收集到了⾜够的环境噪声为⽌。

当然你也可以设置成不堵塞,当你在open的时候设置参数O_NONBLOCK,但是当你read的时候,如果熵池空了,会返回-1。

/dev/random的⼀个副本是/dev/urandom(“unlocked”,⾮阻塞的随机数发⽣器),它会重复使⽤熵池中的数据以产⽣伪随机数

据。这表⽰对/dev/urandom的读取操作不会产⽣阻塞,但其输出的熵可能⼩于/dev/random的。它可以作为⽣成较低强度密码的伪随

机数⽣成器,不建议⽤于⽣成⾼强度长期密码。

资料

1.

本文发布于:2023-01-04 17:57:25,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/92057.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:receive怎么读
标签:randomness
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图