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

在c#中创建加密随机数的最快、线程安全的方法?,c#,multithreading,random,parallel-processing,cryptography,C#,Multithreading,Random,Parallel Processing,Cryptography,注意,当在多个线程上并行生成随机数时,加密随机数生成器不是线程安全的。所使用的生成器是RNGCryptoServiceProvider,它似乎重复长时间的随机位(128位)。下面显示了重现此操作的代码 除了使用锁来保护对RNGCryptoServiceProvider实例的访问(这会扼杀这里的整个速度点),还有谁有更快的方法来生成加密随机数吗 using System; using System.Runtime.Caching; using System.Security.Cryptograph

注意,当在多个线程上并行生成随机数时,加密随机数生成器不是线程安全的。所使用的生成器是
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
            }
        }
    }
}
使用系统;
使用System.Runtime.Caching;
使用System.Security.Cryptography;
使用System.Threading.Tasks;
名称空间并行性随机性
{
班级计划
{
静态void Main(字符串[]参数)
{
var测试=新测试();
编写(“序列化的verion正在运行…”);
test.Run(假);
Console.WriteLine();
编写(“并行化的verion运行…”);
test.Run(真);
Console.WriteLine(Environment.NewLine+“Done.”);
Console.ReadLine();
}
}
课堂测试
{
私有只读RNGCryptoServiceProvider _rng=new RNGCryptoServiceProvider();
私有只读字节[]_randomBytes=新字节[128/8];
私有int冲突计数=0;
私有只读对象collisionCountLock=新对象();
公共无效运行(bool并行)
{
const int numOfRuns=100000;
常数int STARTINCLUSTIVE=1;
const int endExclusive=numOfRuns+startInclusive;
if(并行)
Parallel.For(startInclusive,endExclusive,x=>GenRandomByteArray(x));
其他的
{
对于(变量i=startInclusive;i
for
RNGCryptoServiceProvider
声明:

这种类型是线程安全的

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

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


因此,如果您有性能问题,我会尝试的第一件事是一次性请求更多数据,然后自己拆分。如果这还不够,请使用CSPRNG系统植入的流密码。

该问题答案的(部分)可能重复提供了快速且线程安全的随机数生成方法。for
RNGCryptoServiceProvider
文档明确指出:“此类型是线程安全的。”@遗忘:实际上,这个类(
Random
)甚至不是crypto-Random,所以它的线程安全性和性能是无关的。你不是在多个线程中使用同一个数组吗?这显然是不安全的,即使PRNG本身是安全的。无论如何都要使用流密码来扩展初始熵,但必须使用加密CSRNG,而不是不安全的系统PRNG。定期从CSPRNG重新设定种子,这样您就不会从流密码中获得长时间的运行。加密与它最薄弱的环节一样强大,而非加密PRNG则很弱。@rossum所说的系统PRNG,我指的是
/dev/uradom
cryptgenerandom
。不是.net附带的蹩脚的
系统.Random