为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1257299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why use the C# class System.Random at all instead of System.Security.Cryptography.RandomNumberGenerator?
提问by Lernkurve
Why would anybody use the "standard" random number generator from System.Randomat all instead of always using the cryptographically secure random number generator from System.Security.Cryptography.RandomNumberGenerator(or its subclasses because RandomNumberGenerator is abstract)?
为什么有人会使用System.Random的“标准”随机数生成器,而不是总是使用System.Security.Cryptography.RandomNumberGenerator(或其子类,因为 RandomNumberGenerator 是抽象的)的加密安全随机数生成器?
Nate Lawson tells us in his Google Tech Talk presentation "Crypto Strikes Back" at minute 13:11 not to use the "standard" random number generators from Python, Java and C# and to instead use the cryptographically secure version.
Nate Lawson在 13:11 分钟的Google Tech Talk 演讲“ Crypto Strikes Back”中告诉我们不要使用来自 Python、Java 和 C# 的“标准”随机数生成器,而是使用加密安全版本。
I know the difference between the two versions of random number generators (see question 101337).
我知道两个版本的随机数生成器之间的区别(请参阅问题 101337)。
But what rationale is there to not always use the secure random number generator? Why use System.Random at all? Performance perhaps?
但是不总是使用安全随机数生成器的理由是什么?为什么要使用 System.Random?也许性能?
采纳答案by Kevin LaBranche
Speed and intent. If you're generating a random number and have no need for security, why use a slow crypto function? You don't need security, so why make someone else think that the number may be used for something secure when it won't be?
速度和意图。如果您正在生成一个随机数并且不需要安全性,为什么要使用慢速加密功能?您不需要安全性,那么为什么要让其他人认为该号码可能被用于安全的事情,而实际上它不会呢?
回答by Michael
System.Random is much more performant since it does not generate cryptographically secure random numbers.
System.Random 的性能要高得多,因为它不会生成加密安全的随机数。
A simple test on my machine filling a buffer of 4 bytes with random data 1,000,000 times takes 49 ms for Random, but 2845 ms for RNGCryptoServiceProvider. Note that if you increase the size of the buffer you are filling, the difference narrows as the overhead for RNGCryptoServiceProvider is less relevant.
在我的机器上用随机数据填充 4 个字节的缓冲区 1,000,000 次的简单测试对于 Random 需要 49 毫秒,对于 RNGCryptoServiceProvider 需要 2845 毫秒。请注意,如果您增加正在填充的缓冲区的大小,则差异会缩小,因为 RNGCryptoServiceProvider 的开销不太相关。
回答by Henk Holterman
Apart from the speed and the more useful interface (NextDouble()
etc) it is also possible to make a repeatable random sequence by using a fixed seed value. That is quite useful, amongst others during Testing.
除了速度和更有用的界面(NextDouble()
等)之外,还可以通过使用固定的种子值来制作可重复的随机序列。这在测试期间非常有用。
Random gen1 = new Random(); // auto seeded by the clock
Random gen2 = new Random(0); // Next(10) always yields 7,8,7,5,2,....
回答by tvanfosson
If I don't need the security, i.e., I just want a relatively indeterminate value not one that's cryptographically strong, Random has a much easier interface to use.
如果我不需要安全性,即,我只想要一个相对不确定的值,而不是加密强度高的值,那么 Random 有一个更容易使用的界面。
回答by nos
Not everyone needs cryptographically secure random numbers, and they might benefit more from a speedier plain prng. Perhaps more importantly is that you can control the sequence for System.Random numbers.
不是每个人都需要加密安全的随机数,他们可能会从更快的普通 prng 中受益更多。也许更重要的是您可以控制 System.Random 数字的序列。
In a simulation utilizing random numbers you might want to recreate, you rerun the simulation with the same seed. It can be handy for tracking bugs when you want to regenerate a given faulty scenario as well - running your program with the exact same sequence of random numbers that crashed the program.
在使用您可能想要重新创建的随机数的模拟中,您可以使用相同的种子重新运行模拟。当您还想重新生成给定的错误场景时,它可以方便地跟踪错误 - 使用与程序崩溃的完全相同的随机数序列运行您的程序。
回答by Dan Diplo
If you're programming an online card game or lotter then you would want to make sure the sequence is next to impossible to guess. However, if you are showing users, say, a quote of the day the performance is more important than security.
如果您正在编写在线纸牌游戏或彩票,那么您需要确保几乎不可能猜到顺序。但是,如果您向用户展示当天的报价,则性能比安全性更重要。
回答by J?rg W Mittag
The most obvious reasons have already been mentioned, so here's a more obscure one: cryptographic PRNGs typically need to be continually be reseeded with "real" entropy. Thus, if you use a CPRNG too often, you could deplete the system's entropy pool, which (depending on the implementation of the CPRNG) will either weaken it (thus allowing an attacker to predict it) or it will block while trying to fill up its entropy pool (thus becoming an attack vector for a DoS attack).
已经提到了最明显的原因,所以这里有一个更模糊的原因:加密 PRNG 通常需要不断地用“真实”熵重新播种。因此,如果您过于频繁地使用 CPRNG,您可能会耗尽系统的熵池,这(取决于 CPRNG 的实现)会削弱它(从而允许攻击者预测它)或者它会在尝试填满时阻塞它的熵池(因此成为 DoS 攻击的攻击向量)。
Either way, your application has now become an attack vector for other, totally unrelated applications which?– unlike yours?– actually vitally dependon the cryptographic properties of the CPRNG.
无论哪种方式,您的应用程序现在已经成为其他完全不相关的应用程序的攻击媒介,这些应用程序——与您的不同?——实际上非常依赖于 CPRNG 的加密属性。
This is an actual real world problem, BTW, that has been observed on headless servers (which naturally have rather small entropy pools because they lack entropy sources such as mouse and keyboard input) running Linux, where applications incorrectly use the /dev/random
kernel CPRNG for all sorts of random numbers, whereas the correct behavior would be to read a small seed value from /dev/urandom
and use that to seed their ownPRNG.
这是一个实际的现实世界问题,顺便说一句,在运行 Linux 的无/dev/random
外设服务器(它们自然具有相当小的熵池,因为它们缺乏诸如鼠标和键盘输入之类的熵源)上已经观察到,其中应用程序错误地将内核 CPRNG 用于各种随机数,而正确的行为是从中读取一个小的种子值/dev/urandom
并使用它来播种他们自己的PRNG。
回答by J?rg W Mittag
This has been discussed at some length, but ultimately, the issue of performance is a secondary consideration when selecting a RNG. There are a vast array of RNGs out there, and the canned Lehmer LCG that most system RNGs consists of is not the best nor even necessarily the fastest. On old, slow systems it was an excellent compromise. That compromise is seldom ever really relevant these days. The thing persists into present day systems primarily because A) the thing is already built, and there is no real reason to 'reinvent the wheel' in this case, and B) for what the vast bulk of people will be using it for, it's 'good enough'.
这已经讨论了一段时间,但最终,性能问题是选择 RNG 时的次要考虑因素。有大量的 RNG,而大多数系统 RNG 所包含的罐装 Lehmer LCG 并不是最好的,甚至不一定是最快的。在旧的、缓慢的系统上,这是一个很好的折衷方案。如今,这种妥协很少真正相关。这个东西在当今的系统中持续存在,主要是因为 A)这个东西已经建成,在这种情况下没有真正的理由“重新发明轮子”,B)对于绝大多数人将使用它的目的,它是'够好了'。
Ultimately, the selection of an RNG comes down to Risk/Reward ratio. In some applications, for example a video game, there is no risk whatsoever. A Lehmer RNG is more than adequate, and is small, concise, fast, well-understood, and 'in the box'.
最终,RNG 的选择归结为风险/回报比率。在某些应用程序中,例如视频游戏,没有任何风险。Lehmer RNG 绰绰有余,体积小、简洁、快速、易于理解,并且“在盒子里”。
If the application is, for example, an on-line poker game or lottery where there are actual prizes involved and real money comes into play at some point in the equation, the 'in the box' Lehmer is no longer adequate. In a 32-bit version, it only has 2^32 possible valid states before it begins to cycle at best. These days, that's an open door to a brute force attack. In a case like this, the developer will want to go to something like a Very Long PeriodRNG of some species, and probably seed it from a cryptographically strong provider. This gives a good compromise between speed and security. In such a case, the person will be out looking for something like the Mersenne Twister, or a Multiple Recursive Generatorof some kind.
例如,如果应用程序是在线扑克游戏或彩票,其中涉及实际奖品并且真钱在等式中的某个时刻发挥作用,那么“盒子里”的莱默就不再适用了。在32位版本,它只有2 ^ 32个可能的有效状态就开始循环之前,最好。如今,这是暴力攻击的大门。在这种情况下,开发人员将希望使用某些物种的超长周期RNG 之类的东西,并可能从密码学强大的提供者处为其播种。这在速度和安全性之间提供了很好的折衷。在这种情况下,这个人会出去寻找类似Mersenne Twister或某种多重递归生成器的东西。
If the application is something like communicating large quantities of financial information over a network, now there is a huge risk, and it heavily outweights any possible reward. There are still armored cars because sometimes heavily armed men is the only security that's adequate, and trust me, if a brigade of special ops people with tanks, fighters, and helicopters was financially feasible, it would be the method of choice. In a case like this, using a cryptographically strong RNG makes sense, because whatever level of security you can get, it's not as much as you want. So you'll take as much as you can find, and the cost is a very, very remote second-place issue, either in time or money. And if that means that every random sequence takes 3 seconds to generate on a very powerful computer, you're going to wait the 3 seconds, because in the scheme of things, that is a trivial cost.
如果应用程序类似于通过网络传递大量财务信息,那么现在存在巨大风险,而且它的权重远远超过任何可能的回报。仍然有装甲车,因为有时全副武装的人是唯一足够的安全措施,相信我,如果一支配备坦克、战斗机和直升机的特种作战旅在经济上可行,这将是首选方法。在这种情况下,使用加密强 RNG 是有道理的,因为无论您可以获得什么级别的安全性,它都不是您想要的。所以你会尽可能多地拿走,而成本是一个非常非常遥远的次要问题,无论是时间还是金钱。如果这意味着在非常强大的计算机上生成每个随机序列需要 3 秒,那么您将等待 3 秒,
回答by quant_dev
Different needs call for different RNGs. For crypto, you want your random numbers to be as random as possible. For Monte Carlo simulations, you want them to fill the space evenly and to be able to start the RNG from a known state.
不同的需求需要不同的 RNG。对于加密,您希望随机数尽可能随机。对于 Monte Carlo 模拟,您希望它们均匀地填充空间并能够从已知状态启动 RNG。
回答by CodesInChaos
First of all the presentation you linked only talks about random numbers for security purposes. So it doesn't claim Random
is bad for non security purposes.
首先,您链接的演示文稿仅出于安全目的讨论随机数。因此,它不会声称Random
出于非安全目的是不好的。
But I do claim it is. The .net 4 implementation of Random
is flawed in several ways. I recommend only using it if you don't care about the quality of your random numbers. I recommend using better third party implementations.
但我确实声称它是。.net 4 的实现Random
在几个方面存在缺陷。如果您不关心随机数的质量,我建议仅使用它。我建议使用更好的第三方实现。
Flaw 1: The seeding
缺陷一:播种
The default constructor seeds with the current time. Thus all instances of Random
created with the default constructor within a short time-frame (ca. 10ms) return the same sequence. This is documented and "by-design". This is particularly annoying if you want to multi-thread your code, since you can't simply create an instance of Random
at the beginning of each thread's execution.
当前时间的默认构造函数种子。因此,Random
在短时间内(大约 10 毫秒)内使用默认构造函数创建的所有实例都返回相同的序列。这是记录在案和“按设计”。如果您想对代码进行多线程处理,这尤其令人讨厌,因为您不能简单地Random
在每个线程的执行开始时创建 的实例。
The workaround is to be extra careful when using the default constructor and manually seed when necessary.
解决方法是在使用默认构造函数时要格外小心,并在必要时手动设置种子。
Another problem here is that the seed space is rather small (31 bits). So if you generate 50k instances of Random
with perfectly random seeds you will probably get one sequence of random numbers twice (due to the birthday paradox). So manual seeding isn't easy to get right either.
这里的另一个问题是种子空间相当小(31 位)。所以如果你Random
用完全随机的种子生成 50k 个实例,你可能会得到一个随机数序列两次(由于生日悖论)。因此,手动播种也不容易正确。
Flaw 2: The distribution of random numbers returned by Next(int maxValue)
is biased
缺陷二:返回的随机数分布Next(int maxValue)
有偏
There are parameters for which Next(int maxValue)
is clearly not uniform. For example if you calculate r.Next(1431655765) % 2
you will get 0
in about 2/3 of the samples. (Sample code at the end of the answer.)
有些参数Next(int maxValue)
显然是不统一的。例如,如果您进行计算,r.Next(1431655765) % 2
您将获得0
大约 2/3 的样本。(答案末尾的示例代码。)
Flaw 3: The NextBytes()
method is inefficient.
缺陷三:NextBytes()
方法效率低下。
The per byte cost of NextBytes()
is about as big as the cost to generate a full integer sample with Next()
. From this I suspect that they indeed create one sample per byte.
的每字节成本NextBytes()
大约与使用 生成完整整数样本的成本一样大Next()
。由此我怀疑他们确实为每个字节创建了一个样本。
A better implementation using 3 bytes out of each sample would speed NextBytes()
up by almost a factor 3.
使用每个样本中的 3 个字节的更好实现将使速度NextBytes()
提高近 3 倍。
Thanks to this flaw Random.NextBytes()
is only about 25% faster than System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
on my machine (Win7, Core i3 2600MHz).
由于这个缺陷Random.NextBytes()
,它只比System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
我的机器(Win7,Core i3 2600MHz)快 25% 。
I'm sure if somebody inspected the source/decompiled byte code they'd find even more flaws than I found with my black box analysis.
我敢肯定,如果有人检查了源代码/反编译的字节码,他们会发现比我在黑盒分析中发现的更多缺陷。
Code samples
代码示例
r.Next(0x55555555) % 2
is strongly biased:
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]);
Performance:
表现:
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);
}