快速创建 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 是 RNGCryptoServiceProviderSecureRandom 存储在静态字段中并且仅初始化一次)。因此,创建 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版本不是这样的,不确定。