快速创建 BouncyCastle SecureRandom 实例是否有问题?
Is rapidly creating BouncyCastle SecureRandom instances problematic?
如 Random number generator only generating one random number 所述,每次需要另一个随机数时都创建 System.Random
的新实例通常是不正确的,因为 System.Random
是基于时钟和因此在同一个 tick 中创建的多个实例将产生相同的随机数。因此,一种常见的做法(至少在单线程应用程序中)是创建 Random
的单个实例,存储在用于所有随机数生成的静态字段中。
另一方面,RNGCryptoServiceProvider
没有这个特殊缺陷......但显然是 costly to instantiate,因此再次建议存储和重用它的单个实例。
Org.BouncyCastle.Security.SecureRandom
怎么样?我是否同样需要存储和重用它的单个实例,或者每次我需要另一个随机数时按需创建实例基本上都可以吗?
我们可以再次(如相关问题)查看源代码以得出一些结论(SecureRandom源代码供参考)。
构造函数中的所有工作都用于创建伪随机生成器:
private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
{
IDigest digest = DigestUtilities.GetDigest(digestName);
if (digest == null)
return null;
DigestRandomGenerator prng = new DigestRandomGenerator(digest);
if (autoSeed)
{
prng.AddSeedMaterial(NextCounterValue());
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
}
return prng;
}
创建摘要(散列)不需要任何成本(相对于其他工作)。例如 Sha256Digest
默认情况下(使用空构造函数)只分配小的 byte[]
缓冲区。创建 DigestRandomGenerator
本身也没有任何成本(耦合小缓冲区)。完成的主要工作在这里:
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
它使用"master" RNG 来生成种子值。完整 .NET 平台上的主 RNG 是 RNGCryptoServiceProvider
(SecureRandom
存储在静态字段中并且仅初始化一次)。因此,创建 SecureRandom
时的所有工作都用于为伪 RNG 创建加密随机种子。
我想说,最好不要每次都创建新实例,至少对于小生成(对于一两次 NextInt()
调用),因为如果你为每个生成的数字创建新实例 - 你本质上是成本的两倍(一次为种子生成加密随机数,一次生成目标随机数)。因为(据我所知),SecureRandom
是线程安全的——没有太多理由不重用一个实例。
旁注 - 我认为 RNGCryptoServiceProvider
并不像您的 link 声称的那样繁重。它的构造函数是这样的:
public RNGCryptoServiceProvider()
: this((CspParameters) null)
{
}
[SecuritySafeCritical]
public RNGCryptoServiceProvider(CspParameters cspParams)
{
if (cspParams != null)
{
this.m_safeProvHandle = Utils.AcquireProvHandle(cspParams);
this.m_ownsHandle = true;
}
else
{
// we are interested in this path
this.m_safeProvHandle = Utils.StaticProvHandle;
this.m_ownsHandle = false;
}
}
因此,当您创建新实例(不提供 csp)时 - 它会重复使用相同的 Utils.StaticProvHandle
,因此它会使用 RNG 提供程序的相同 "unmanaged" 实例。这反过来意味着创建新实例和重用同一实例在性能上没有差异。可能以前的.NET版本不是这样的,不确定。
如 Random number generator only generating one random number 所述,每次需要另一个随机数时都创建 System.Random
的新实例通常是不正确的,因为 System.Random
是基于时钟和因此在同一个 tick 中创建的多个实例将产生相同的随机数。因此,一种常见的做法(至少在单线程应用程序中)是创建 Random
的单个实例,存储在用于所有随机数生成的静态字段中。
RNGCryptoServiceProvider
没有这个特殊缺陷......但显然是 costly to instantiate,因此再次建议存储和重用它的单个实例。
Org.BouncyCastle.Security.SecureRandom
怎么样?我是否同样需要存储和重用它的单个实例,或者每次我需要另一个随机数时按需创建实例基本上都可以吗?
我们可以再次(如相关问题)查看源代码以得出一些结论(SecureRandom源代码供参考)。
构造函数中的所有工作都用于创建伪随机生成器:
private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
{
IDigest digest = DigestUtilities.GetDigest(digestName);
if (digest == null)
return null;
DigestRandomGenerator prng = new DigestRandomGenerator(digest);
if (autoSeed)
{
prng.AddSeedMaterial(NextCounterValue());
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
}
return prng;
}
创建摘要(散列)不需要任何成本(相对于其他工作)。例如 Sha256Digest
默认情况下(使用空构造函数)只分配小的 byte[]
缓冲区。创建 DigestRandomGenerator
本身也没有任何成本(耦合小缓冲区)。完成的主要工作在这里:
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
它使用"master" RNG 来生成种子值。完整 .NET 平台上的主 RNG 是 RNGCryptoServiceProvider
(SecureRandom
存储在静态字段中并且仅初始化一次)。因此,创建 SecureRandom
时的所有工作都用于为伪 RNG 创建加密随机种子。
我想说,最好不要每次都创建新实例,至少对于小生成(对于一两次 NextInt()
调用),因为如果你为每个生成的数字创建新实例 - 你本质上是成本的两倍(一次为种子生成加密随机数,一次生成目标随机数)。因为(据我所知),SecureRandom
是线程安全的——没有太多理由不重用一个实例。
旁注 - 我认为 RNGCryptoServiceProvider
并不像您的 link 声称的那样繁重。它的构造函数是这样的:
public RNGCryptoServiceProvider()
: this((CspParameters) null)
{
}
[SecuritySafeCritical]
public RNGCryptoServiceProvider(CspParameters cspParams)
{
if (cspParams != null)
{
this.m_safeProvHandle = Utils.AcquireProvHandle(cspParams);
this.m_ownsHandle = true;
}
else
{
// we are interested in this path
this.m_safeProvHandle = Utils.StaticProvHandle;
this.m_ownsHandle = false;
}
}
因此,当您创建新实例(不提供 csp)时 - 它会重复使用相同的 Utils.StaticProvHandle
,因此它会使用 RNG 提供程序的相同 "unmanaged" 实例。这反过来意味着创建新实例和重用同一实例在性能上没有差异。可能以前的.NET版本不是这样的,不确定。