快速创建BouncyCastle SecureRandom实例是否有问题?

时间:2017-10-17 14:17:20

标签: c# random cryptography bouncycastle

Random number generator only generating one random number所述,每次需要另一个随机数时,创建System.Random的新实例通常是不正确的,因为System.Random是根据时钟播种的,因此多个在相同的tick中创建的实例将产生相同的随机数。因此,一种常见做法(至少在单线程应用程序中)是创建一个存储在静态字段中的Random实例,用于生成所有随机数。

另一方面,

RNGCryptoServiceProvider没有这个特殊的缺陷......但显然是costly to instantiate,因此再次建议存储和重用它的单个实例。

Org.BouncyCastle.Security.SecureRandom怎么样?我是否同样需要存储和重用它的单个实例,或者每次我需要另一个随机数时,是否可以根据需要创建实例?

1 个答案:

答案 0 :(得分:2)

我们可以再次(比如相关问题)查看源代码以得出一些结论(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很难创建链接声明。它的构造函数如下:

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提供商的实例。这反过来意味着创建新实例并重用相同的实例在性能上没有区别。也许在.NET的早期版本中,它不是这样,不确定。