C# 为什么要使用C类System.Random而不是System.Security.Cryptography.RandomNumberGenerator?

C# 为什么要使用C类System.Random而不是System.Security.Cryptography.RandomNumberGenerator?,c#,.net,cryptography,random,C#,.net,Cryptography,Random,为什么有人会使用来自的“标准”随机数生成器,而不是始终使用来自的加密安全随机数生成器(或其子类,因为RandomNumberGenerator是抽象的) Nate Lawson在13:11分的Google Tech Talk演示文稿中告诉我们,不要使用Python、Java和C#的“标准”随机数生成器,而是使用加密安全版本 我知道两种版本的随机数生成器之间的区别(请参阅) 但不总是使用安全随机数生成器的理由是什么?为什么要使用随机系统?性能可能?System.Random性能更高,因为它不会生成

为什么有人会使用来自的“标准”随机数生成器,而不是始终使用来自的加密安全随机数生成器(或其子类,因为RandomNumberGenerator是抽象的)

Nate Lawson在13:11分的Google Tech Talk演示文稿中告诉我们,不要使用Python、Java和C#的“标准”随机数生成器,而是使用加密安全版本

我知道两种版本的随机数生成器之间的区别(请参阅)


但不总是使用安全随机数生成器的理由是什么?为什么要使用随机系统?性能可能?

System.Random性能更高,因为它不会生成加密安全的随机数


在我的机器上进行一个简单的测试,用随机数据填充4字节的缓冲区1000000次,随机需要49毫秒,但RNGCryptoServiceProvider需要2845毫秒。请注意,如果增加要填充的缓冲区的大小,则差异会缩小,因为RNGCryptoServiceProvider的开销不太相关。

速度和意图。如果您正在生成一个随机数,并且不需要安全性,为什么要使用慢速加密函数?你不需要安全性,那么为什么要让其他人认为这个数字可能用于安全的东西,而它不会呢?

除了速度和更有用的接口(
NextDouble()
等)之外,还可以通过使用固定的种子值来生成一个可重复的随机序列。这在测试期间非常有用

Random gen1 = new Random();     // auto seeded by the clock
Random gen2 = new Random(0);    // Next(10) always yields 7,8,7,5,2,....

如果我不需要安全性,也就是说,我只想要一个相对不确定的值,而不是一个加密性很强的值,Random有一个更容易使用的接口。

不是每个人都需要加密安全的随机数,而且它们可能会从更快的普通prng中受益更多。也许更重要的是,您可以控制系统随机数的顺序


在使用可能要重新创建的随机数的模拟中,使用相同的种子重新运行模拟。当你想重新生成一个给定的错误场景时,它可以很方便地跟踪错误——用与程序崩溃完全相同的随机数序列运行程序。

如果你正在编写一个在线纸牌游戏或彩票,那么你需要确保序列几乎不可能被猜到。但是,如果您向用户展示当天的一句话,那么性能比安全性更重要。

最明显的原因已经提到,所以这里有一个更模糊的原因:加密PRNG通常需要不断地重新设定“真实”熵。因此,如果你经常使用CPRNG,你可以耗尽系统的熵池,这取决于CPRNG的实现,或者削弱它(从而允许攻击者预测它),或者在试图填充它的熵池时它会阻塞(从而成为DoS攻击的攻击向量)。

无论哪种方式,你的应用程序现在已经成为其他完全无关的应用程序的攻击向量,它与你的应用程序不同,实际上非常依赖于CPRNG的密码特性。


顺便说一句,这是一个实际的现实问题,在运行Linux的无头服务器上(由于缺少诸如鼠标和键盘输入之类的熵源,这些服务器自然具有相当小的熵池),应用程序错误地将
/dev/random
内核CPRNG用于各种随机数,然而,正确的做法是从
/dev/uradom
读取一个小的种子值,并使用该值为自己的PRNG种子。

这一点已经进行了详细讨论,但最终,在选择RNG时,性能问题是次要考虑因素。那里有大量的RNG,大多数系统RNG组成的罐装莱默LCG不是最好的,甚至不一定是最快的。在旧的、缓慢的系统上,这是一个极好的折衷方案。如今,这种妥协很少真正相关。这个东西一直存在于当今的系统中,主要是因为A)这个东西已经建造好了,在这种情况下没有真正的理由“重新发明轮子”,B)对于大多数人使用它的目的,它“足够好了”

最终,RNG的选择取决于风险/回报比率。在某些应用中,例如视频游戏,没有任何风险。Lehmer RNG非常合适,而且体积小、简洁、快速、易于理解且“在盒子里”

例如,如果应用程序是一个在线扑克游戏或彩票,其中涉及到实际的奖品,并且在等式中的某个点有真正的钱投入,那么“盒子里的”莱默就不再足够了。在32位版本中,它最多只有2^32个可能的有效状态才能开始循环。现在,这是一个开放的大门,以暴力攻击。在这样的情况下,开发人员将希望使用一些物种的非常长的RNG,并可能从加密功能强大的提供商那里获取种子。这在速度和安全性之间达成了很好的折衷。在这种情况下,用户将寻找类似于Mersenne Twister或某种多重递归生成器的东西

如果应用程序类似于通过网络传输大量金融信息,那么现在就存在着巨大的风险,并且大大超过了任何可能的回报。现在仍然有装甲车,因为有时全副武装的人是唯一足够的安全保障。相信我,如果一个拥有坦克、战斗机和直升机的特种部队在经济上可行的话,这将是一种选择。在这种情况下,使用加密性强的RNG使sen
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);
}