【问题标题】:Is rapidly creating BouncyCastle SecureRandom instances problematic?快速创建 BouncyCastle SecureRandom 实例是否存在问题?
【发布时间】:2018-03-29 06:38:30
【问题描述】:

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

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

Org.BouncyCastle.Security.SecureRandom 怎么样?我是否同样需要存储和重用它的单个实例,或者每次我需要另一个随机数时按需创建实例基本上没问题?

【问题讨论】:

  • 相关(因为它也是关于如何正确使用此类的未记录基础知识):stackoverflow.com/q/46792243/1709587
  • 我认为这取决于您在实践中需要随机数的频率。在我自己的一个项目中,我使用RNGCryptoServiceProvider.Create() 为该站点的每个新用户生成会话令牌,并且性能每天处理 1,000 次访问。那么你多久需要一个随机数呢?
  • 从您评论中链接的答案中,您最终会为每个 SecureRandom 实例实例化一个新的 RNGCryptoServiceProvider。除了这个表现之外,它看起来还不错。
  • @JamesKPolk 实际上Master 随机生成器(使用RNGCryptoServiceProvider)是静态单例,所以它被初始化一次,而不是每次创建SecureRandom。我不认为确实如此,创建new RNGCryptoServiceProvider() 的成本很高。在内部它使用非托管 api,如果您使用默认构造函数 - 它将使用相同的非托管“句柄”,基本上在重用一个实例和每次创建新实例之间没有区别(因为它们都使用相同的非托管“句柄”)。
  • @Evk:感谢您纠正我,并感谢您在其他链接上的回答。在这种情况下,应该既不存在安全问题,也不存在性能问题。

标签: c# random cryptography bouncycastle


【解决方案1】:

我们可以再次(如在相关问题中)查看源代码以得出一些结论(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()));

它使用“主”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 版本中不是这样,不确定。

【讨论】:

    猜你喜欢
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    相关资源
    最近更新 更多