随机数生成器与RNGCryptoServiceProvider

26
根据MSDN文档中RandomNumberGenerator的说明:
应用程序代码不直接使用此类。此抽象类作为所有加密随机数生成器的基类提供。
要实现加密随机数生成器,请使用派生类RNGCryptoServiceProvider。
然而,我在不同的代码库中看到过几次使用以下代码:
byte[] bytes = new byte[...];
RandomNumberGenerator rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);

最值得注意的是,它与 StackExchange(我认为包括 SO)和 BCrypt.Net 一起使用。

因此,我有点困惑 - 上述代码返回什么类型的 RandomNumberGenerator? 此外,某些代码库确实使用RandomNumberGenerator而不是RNGCryptoServiceProvider是否有点缺陷?

我假设RandomNumberGenerator.Create()在幕后执行,但在这里我完全错过了什么,技术上来说(因为它是一个抽象类),上面的代码不应该抛出错误吗?


2
值得指出的是,RNGCryptoServiceProvider类现已过时,建议改用RandomNumberGenerator静态方法。 - badjuice
3个回答

28
RandomNumberGenerator.Create() 方法调用 RandomNumberGenerator.Create("System.Security.Cryptography.RandomNumberGenerator"),它最终会创建一个 RNGCryptoServiceProvider 实例。(它在一对字典中进行了一些查找,因此您可以通过在某处注册默认的随机生成器来更改该调用的行为。)
实际返回的对象类型在编译时是不知道的,只知道它将继承RandomNumberGenerator类,因此您可以使用RandomNumberGenerator引用变量进行操作。
这种根据输入创建不同类型实例的方式在框架中的一些地方都被使用,例如WebRequest.Create方法。
微软中有人“修正”了当前(.NET Framework 4.5)Create()方法的文档。现在它说:
  

“当在派生类中重写时,创建加密随机数生成器的默认实现的实例,可用于生成随机数据。”

框架4.0的文档如下:
  

“创建一个默认实现的加密随机数生成器的实例,可用于生成随机数据。”

这是该方法的正确描述。我将提出请求将该描述放回更新的文档中。

“which will eventually create an instance of RNGCryptoServiceProvider” - 不过这是根据什么标准呢?为什么不是特别使用 AesCryptoServiceProvider/SHA512/DAS 等等呢?这是否只是与 RandomNumberGenerator 采用的相同方法,即在内部决定将使用哪种算法? - James
1
@James:嗯,基本上这是因为这是唯一默认实现的随机数生成器(请参阅继承层次结构http://msdn.microsoft.com/en-us/library/42ks8fz1)。`AesCryptuServiceProvider`是一个加密算法,而不是随机数生成器。随机数生成器并不特定于不同的加密算法。允许注册不同的生成器的唯一原因是有人可能发现当前实现产生的随机数不够好。 - Guffa
哎呀,那是我的错误,我假设所有的CryptoServiceProvider都是随机数生成器!实际上,框架中只有一个随机数生成器的实现,即RNGCryptoServiceProvider,因此,使用哪个都无所谓? - James
4
根据目前的了解,缺省的 RNG 是最好的选择。将来的框架中可能会用不同的实现替换 RNGCryptoServiceProvider,那时 Create() 方法会返回新的实现。如果发现 RNGCryptoServiceProvider 太弱,可以创建一个特定的提供程序,但现在无法做任何事情,使用 Create() 方法是最具未来性的解决方案。 - Guffa
RandomNumberGenerator.Create() 是线程安全的吗? - variable
从.NET Core 6开始,RNGCryptoServiceProvider被标记为过时(因此不应该显式实例化)。 RandomNumberGenerator.Create()似乎是获取随机数生成器的“官方”方式,返回的对象显示为System.Security.Cryptography.RandomNumberGeneratorImplementation - David G

4
RandomNumberGenerator的文档实际上非常混乱。例如,有这样的文档:

在派生类中重写时,创建指定实现的密码随机数生成器的实例。

...针对一个静态方法。静态方法是无法被重写的。可以看出编写文档的人思路不清晰。

我猜测最初的意图可能是这样的:

应用程序代码不直接实例化此类。此抽象类作为所有密码随机数生成器的基类提供。

我认为您发布的代码(使用静态Create方法)是完全合理的。这与XmlReader.Create等使用的模式相同-静态方法选择最合适的实现。


1
“静态方法选择最合适的实现” - 有点令人沮丧的是,没有文档说明哪个版本是最合适的标准。 - James

2

RandomNumberGenerator.Create 是一个静态工厂方法。它会返回一个派生类的实例,而这个派生类是非抽象的,所以这样做是合法的。

抽象类被设计成可以用于替换更具体的类的所有地方。它们旨在提供一个友好的接口,以便进行版本控制。


实际返回的类型可能会有所变化。您可以在web.config中覆盖它(没有参考文献在手,但我的记忆和Reflector都表明是这种情况)。对于返回类型没有任何保证。具体类型是否对您有影响? - usr
我同意。这是一个文档缺陷。 - usr
只是为了澄清您的观点,文档确实指出:“您无法创建抽象类的实例。应用程序代码将创建派生类的新实例”。然而,我的问题更多地是针对它将创建哪种类型的派生类。 - James
RandomNumberGenerator.Create() 是线程安全的吗? - variable
@variable 是的,在.NET中,静态方法通常是线程安全的,因此在这种特殊情况下也是如此。不同的应用程序组件和库要同步访问静态方法非常困难,它们必须是线程安全的。 - usr
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接