Ler*_*rve 77 .net c# random cryptography
为什么有人会使用System.Random中的"标准"随机数生成器,而不是总是使用System.Security.Cryptography.RandomNumberGenerator(或其子类,因为RandomNumberGenerator是抽象的)的加密安全随机数生成器?
Nate Lawson 在13:11分钟的Google Tech Talk演讲中告诉我们" Crypto Strikes Back ",不要使用Python,Java和C#中的"标准"随机数生成器,而是使用加密安全版本.
我知道两个版本的随机数生成器之间的区别(参见问题101337).
但是,有什么理由不总是使用安全随机数发生器?为什么要使用System.Random?性能或许?
Kev*_*che 136
速度和意图.如果您生成一个随机数并且不需要安全性,为什么要使用慢速加密功能?您不需要安全性,那么为什么要让其他人认为这个数字可能用于安全的东西呢?
Hen*_*man 63
除了速度和更有用的界面(NextDouble()
等)之外,还可以通过使用固定的种子值来制作可重复的随机序列.这在测试期间非常有用.
Random gen1 = new Random(); // auto seeded by the clock
Random gen2 = new Random(0); // Next(10) always yields 7,8,7,5,2,....
Run Code Online (Sandbox Code Playgroud)
Cod*_*aos 50
首先,您链接的演示文稿仅出于安全目的而谈论随机数.因此,Random
对于非安全目的,它不会声称是坏的.
但我确实声称它是..net 4的实现Random
在几个方面存在缺陷.如果您不关心随机数的质量,我建议仅使用它.我建议使用更好的第三方实现.
缺陷1:播种
默认构造函数种子与当前时间.因此,Random
在短时间范围内(约10ms)使用默认构造函数创建的所有实例都返回相同的序列.这是记录和"按设计".如果你想多线程化你的代码,这尤其令人讨厌,因为你不能简单地Random
在每个线程的执行开始时创建一个实例.
解决方法是在使用默认构造函数时要格外小心,并在必要时手动播种.
这里的另一个问题是种子空间相当小(31位).因此,如果你Random
使用完全随机的种子生成50k个实例,你可能会得到两个随机数的序列两次(由于生日悖论).所以人工播种也不容易.
缺陷2:返回的随机数的分布Next(int maxValue)
是有偏差的
有些参数Next(int maxValue)
显然不均匀.例如,如果您计算,r.Next(1431655765) % 2
您将获得0
大约2/3的样本.(答案末尾的示例代码.)
缺陷3:该NextBytes()
方法效率低下.
每字节成本NextBytes()
大约与生成完整整数样本的成本一样大Next()
.由此我怀疑他们确实每字节创建一个样本.
使用每个样本中的3个字节的更好实现将加速NextBytes()
几乎3倍.
由于这个缺陷Random.NextBytes()
只比System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
我的机器(Win7,Core i3 2600MHz)快约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]);
Run Code Online (Sandbox Code Playgroud)
性能:
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);
}
Run Code Online (Sandbox Code Playgroud)
Mic*_*ael 23
System.Random的性能要高得多,因为它不会生成加密安全的随机数.
在我的机器上用随机数据1,000,000次填充4字节缓冲区的简单测试对于Random来说需要49 ms,对于RNGCryptoServiceProvider需要2845 ms.请注意,如果增加要填充的缓冲区的大小,则差异会缩小,因为RNGCryptoServiceProvider的开销不太相关.
Jör*_*tag 20
最明显的原因已经提到了,所以这里有一个更加模糊的原因:加密的PRNG通常需要不断地用"真实"熵重新接种.因此,如果您经常使用CPRNG,您可能会耗尽系统的熵池(这取决于CPRNG的实现)会削弱它(从而允许攻击者预测它),或者它会在尝试填充时阻塞它的熵池(因此成为DoS攻击的攻击媒介).
无论哪种方式,您的应用程序现在已经成为其他完全不相关的应用程序的攻击载体 - 与您的应用程序不同 - 实际上在很大程度上依赖于CPRNG的加密属性.
这是一个真实的现实世界问题,BTW,已经在无头服务器上观察到(它自然具有相当小的熵池,因为它们缺少熵源,如鼠标和键盘输入)运行Linux,其中应用程序错误地使用/dev/random
内核CPRNG进行各种排序随机数,而正确的行为是从中读取一个小的种子值,/dev/urandom
并用它来种子自己的 PRNG.
evo*_*obe 10
请注意,C#中的System.Random类编码不正确,因此应避免使用.
https://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug#tabs
小智 9
这已在一定程度上进行了讨论,但最终,在选择RNG时,性能问题是次要考虑因素.那里有大量的RNG,大多数系统RNG组成的罐装Lehmer LCG不是最好的,也不一定是最快的.在旧的,缓慢的系统上,这是一个很好的妥协.这些妥协现在很少真正具有相关性.这个东西一直存在于今天的系统中,主要是因为A)这个东西已经建好了,并且没有真正的理由在这种情况下"重新发明轮子",B)因为大部分人都将使用它,它是'够好了'.
最终,RNG的选择归结为风险/回报率.在某些应用中,例如视频游戏,不存在任何风险.Lehmer RNG绰绰有余,小巧,简洁,快速,易于理解,并且"在盒子里".
例如,如果应用程序是在线扑克游戏或彩票,其中涉及实际的奖品,并且在等式中的某个点上真正的金钱发挥作用,那么"在盒子里"Lehmer已不再适用.在32位版本,它只有2 ^ 32就开始周期之前可能的有效状态最好.这些天,这是对暴力攻击敞开的大门.在这种情况下,开发人员会想要某些物种的非常长时间的 RNG,并且可能从加密强大的提供者那里播种.这在速度和安全性之间提供了良好的折衷.在这种情况下,该人将会寻找像Mersenne Twister或某种多重递归生成器这样的东西.
如果应用程序类似于通过网络传递大量财务信息,那么现在存在巨大的风险,并且它会大大超过任何可能的奖励.还有装甲车,因为有时候全副武装的人是唯一足够安全的人,相信我,如果一个拥有坦克,战斗机和直升机的特殊行动人员在经济上可行,那将是首选方法.在这种情况下,使用加密强大的RNG是有道理的,因为无论您获得什么级别的安全性,它都没有您想要的那么多.因此,您可以尽可能多地找到,而且成本是一个非常非常偏远的第二位问题,无论是时间还是金钱.如果这意味着每个随机序列需要3秒才能在非常强大的计算机上生成,那么您将等待3秒钟,因为在方案中,这是一个微不足道的成本.
归档时间: |
|
查看次数: |
43298 次 |
最近记录: |