为什么会有人使用System.Random的“标准”随机数生成器,而不是总是使用System.Security.Cryptography.RandomNumberGenerator的加密安全的随机数生成器(或者它的子类,因为RandomNumberGenerator是抽象的)?
Nate Lawson在13:11分钟的Google Tech Talk演示文稿"Crypto Strikes Back“中告诉我们,不要使用来自Python、Java和C#的”标准“随机数生成器,而要使用加密安全版本。
我知道两个版本的随机数生成器之间的区别(参见question 101337)。
但是,有什么理由不总是使用安全随机数生成器呢?为什么要使用System.Random呢?也许是性能吧?
发布于 2009-08-10 21:29:48
速度和意图。如果您正在生成随机数,并且不需要安全性,为什么要使用速度较慢的加密函数呢?你不需要安全,所以为什么要让别人认为这个号码可能会被用于安全的东西,而它不是这样的呢?
发布于 2009-08-10 21:30:59
除了速度和更有用的接口(NextDouble()
等)之外,还可以通过使用固定的种子值来生成可重复的随机序列。在测试过程中,这是非常有用的。
Random gen1 = new Random(); // auto seeded by the clock
Random gen2 = new Random(0); // Next(10) always yields 7,8,7,5,2,....
发布于 2011-07-27 17:36:31
首先,为了安全起见,您链接的演示文稿只讨论了随机数。所以它不会因为非安全目的而声称Random
是不好的。
但我确实是这么说的。Random
的.net 4实现在几个方面存在缺陷。我建议只有在你不关心随机数质量的情况下才使用它。我建议使用更好的第三方实现。
缺陷1:种子
默认构造函数使用当前时间作为种子。因此,在短时间内(大约10ms)使用默认构造函数创建的所有Random
实例都返回相同的序列。这是文档化的并且是“按设计”的。如果您想对代码进行多线程处理,这尤其令人讨厌,因为您不能简单地在每个线程的执行开始时创建一个Random
实例。
解决方法是在使用默认构造函数时格外小心,并在必要时手动设定种子。
这里的另一个问题是种子空间相当小(31位)。因此,如果您使用完全随机的种子生成50k个Random
实例,您可能会得到一个随机数序列两次(由于birthday paradox)。因此,手动播种也不容易。
缺陷2: Next(int maxValue)
返回的随机数的分布是有偏差的
对于某些参数,Next(int maxValue)
显然不是一致的。例如,如果你计算r.Next(1431655765) % 2
,你将在大约2/3的样本中得到0
。(答案末尾的示例代码。)
缺陷3:NextBytes()
方法效率低下。
NextBytes()
的每字节成本大约与使用Next()
生成完整整数样本的成本一样大。由此,我怀疑他们确实为每个字节创建了一个样本。
一个更好的实现使用每个样本中的3个字节可以将NextBytes()
的速度提高到原来的3倍。
由于这个缺陷,在我的机器(Win7,核心i3 2600 my )上,Random.NextBytes()
仅比System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
快约25%。
我敢肯定,如果有人检查了源代码/反编译的字节代码,他们会发现比我的黑盒分析更多的缺陷。
代码示例
r.Next(0x55555555) % 2
有很强的偏见:
Random r = new Random();
const int mod = 2;
int[] hist = new int[mod];
for(int i = 0; i < 10000000; i++)
{
int num = r.Next(0x55555555);
int num2 = num % 2;
hist[num2]++;
}
for(int i=0;i<mod;i++)
Console.WriteLine(hist[i]);
性能:
byte[] bytes=new byte[8*1024];
var cr=new System.Security.Cryptography.RNGCryptoServiceProvider();
Random r=new Random();
// Random.NextBytes
for(int i=0;i<100000;i++)
{
r.NextBytes(bytes);
}
//One sample per byte
for(int i=0;i<100000;i++)
{
for(int j=0;j<bytes.Length;j++)
bytes[j]=(byte)r.Next();
}
//One sample per 3 bytes
for(int i=0;i<100000;i++)
{
for(int j=0;j+2<bytes.Length;j+=3)
{
int num=r.Next();
bytes[j+2]=(byte)(num>>16);
bytes[j+1]=(byte)(num>>8);
bytes[j]=(byte)num;
}
//Yes I know I'm not handling the last few bytes, but that won't have a noticeable impact on performance
}
//Crypto
for(int i=0;i<100000;i++)
{
cr.GetBytes(bytes);
}
https://stackoverflow.com/questions/1257299
复制相似问题