在 C# 中创建加密随机数的最快、线程安全的方法?

Posted

技术标签:

【中文标题】在 C# 中创建加密随机数的最快、线程安全的方法?【英文标题】:Fastest, thread safe way to create crypto-random numbers in c#? 【发布时间】:2014-07-25 14:58:54 【问题描述】:

注意到当在多个线程上并行生成随机数时,加密随机数生成器不是线程安全的。使用的生成器是RNGCryptoServiceProvider,它似乎重复了长长的随机位(128 位)。重现此的代码如下所示。

没有使用锁来保护对RNGCryptoServiceProvider 实例的访问(这会杀死这里的整个速度点),有没有人有更快的方法来生成加密随机数?

using System;
using System.Runtime.Caching;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace ParallelRandomness

    class Program
    
        static void Main(string[] args)
        
            var test = new Test();
            Console.Write("Serialized verion running ...  ");
            test.Run(false);
            Console.WriteLine();
            Console.Write("Parallelized verion running ...  ");
            test.Run(true);
            Console.WriteLine(Environment.NewLine + "Done.");
            Console.ReadLine();
        
    

    class Test
    
        private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
        private readonly byte[] _randomBytes = new byte[128 / 8];
        private int collisionCount = 0;
        private readonly object collisionCountLock  = new object();

        public void Run(bool parallel)
        
            const int numOfRuns = 100000;
            const int startInclusive = 1;
            const int endExclusive = numOfRuns + startInclusive;
            if (parallel)
                Parallel.For(startInclusive, endExclusive, x => GenRandomByteArrays(x));
            else
            
                for (var i = startInclusive; i < endExclusive; i++)
                    GenRandomByteArrays(i);
            
        

        private void GenRandomByteArrays(long instance)
        
            _rng.GetBytes(_randomBytes);
            var randomString = Convert.ToBase64String(_randomBytes);
            var cache = MemoryCache.Default;
            if (cache.Contains(randomString))
            
                // uh-oh!
                lock (collisionCountLock)
                
                    Console.WriteLine(Environment.NewLine + "Instance 0: Collision count=1. key=2 already in cache. ", instance, ++collisionCount, randomString);
                
            
            else
            
                MemoryCache.Default.Add(randomString, true, DateTimeOffset.UtcNow.AddMinutes(5));
                Console.Write(instance % 2 == 0 ? "\b-" : "\b|"); // poor man's activity indicator
            
        
    

【问题讨论】:

Is C# Random Number Generator thread safe?的可能重复 (部分)该问题的答案提供了快速线程安全的随机数生成方法。 RNGCryptoServiceProvider 的文档明确指出:“这种类型是线程安全的。” @ObliviousSage:实际上那个类 (Random) 甚至不是加密随机的,所以它的线程安全和性能无关紧要。 你不是在多个线程中使用同一个数组吗?即使 PRNG 本身是安全的,这显然也不安全。 【参考方案1】:

documentation 的 RNGCryptoServiceProvider 声明:

这种类型是线程安全的。

您的代码没有证明RNGCryptoServiceProvider 不是线程安全的,因为您在多个线程中使用相同的数组。即使RNGCryptoServiceProvider 是,该数组重用也不是线程安全的。

关于性能,我想指出创建RNGCryptoServiceProvider 的新实例非常便宜。昂贵的部分是GetBytes 的每次调用开销。

因此,如果您遇到性能问题,我会尝试的第一件事是一次性请求更多数据并自行拆分。如果这还不够,请使用系统 CSPRNG 播种的流密码。

【讨论】:

无论如何都要使用流密码来拉伸初始熵,但您必须使用加密 CSRNG 播种,而不是不安全的系统 PRNG。定期从 CSPRNG 重新播种,这样您就不会从流密码中运行太长时间。加密与其最薄弱的环节一样强大,而非加密的 PRNG 很弱。 @rossum 系统 PRNG 我的意思是 /dev/urandomCryptGenRandom。不是 .net 中包含的糟糕的 System.Random

以上是关于在 C# 中创建加密随机数的最快、线程安全的方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建密码重置链接?

哪种 BouncyCastle API 支持的加密算法对于 C# .NET 中的短字符串加密最快且非常安全?

如何在 Java 中创建安全的随机 AES 密钥?

在 C# 中创建动态线程 [关闭]

使用 mptt 在 Python / Django 中创建 JSON 以反映树结构的最快方法

在Java中创建长度为'x'的字符串的最快方法全部由char'c'组成[重复]