RNGCryptoServiceProvider - 随机数查看

cwe*_*ton 26 .net c# asp.net security random

在寻找生成真正随机数的最佳尝试时,我偶然发现了这个代码示例.

在这个片段上寻找意见.

using System;
using System.Security.Cryptography;

private static int NextInt(int min, int max)
{
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] buffer = new byte[4];

    rng.GetBytes(buffer);
    int result = BitConverter.ToInt32(buffer, 0);

    return new Random(result).Next(min, max);
}
Run Code Online (Sandbox Code Playgroud)

资料来源:http://www.vcskicks.com/code-snippet/rng-int.php

这比使用滴答计数种子更受欢迎,例如:

Random rand = new Random(Environment.TickCount); 
rand.Next(min, max);
Run Code Online (Sandbox Code Playgroud)

注意:

我不是在寻找第三方随机数据提供者,例如Random.org,因为这种依赖对应用程序来说是不现实的.

Luk*_*keH 22

好吧,使用RNGCryptoServiceProvider给你一个不可思议的加密力量种子,而Environment.TickCount理论上是可预测的.

NextInt快速连续多次调用您的方法时,另一个重要区别是显而易见的.使用RNGCryptoServiceProviderRandom每次使用不同的加密强度数对种子进行播种,这意味着它将继续为每次调用返回不同的随机数.使用每次使用相同数字TickCount播种Random对象的风险(如果在同一"tick"期间多次调用该方法),这意味着它将继续为每个调用返回相同(假设随机)的数字.

如果你真的需要真正随机的数字,那么你就不应该使用计算机来生成它们:你应该测量放射性衰变或类似的东西,真正无法预测.

  • @Henk:是的,但是在OP的例子中,每个`Random`实例都是单用的,所以我不认为这是一个问题.用加密强度数对RNG种子; 生成一个整数; 丢弃RNG; 根据需要重复.这不是一个理想的设置,但应该相当安全.(当然,如果需要真正的加密强度数,那么OP应该咨询加密专家.) (2认同)
  • 在这里切断,但是,放射性衰变肯定是可以预测的. (2认同)

Eld*_*rov 6

不要使用你的代码.您的解决方案是错误的,并产生差的随机数.我建议我的解决方案,它生成加密强大的随机数:

public class SecureRandom : RandomNumberGenerator
{
    private readonly RandomNumberGenerator rng = new RNGCryptoServiceProvider();


    public int Next()
    {
        var data = new byte[sizeof(int)];
        rng.GetBytes(data);
        return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
    }

    public int Next(int maxValue)
    {
        return Next(0, maxValue);
    }

    public int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException();
        }
        return (int)Math.Floor((minValue + ((double)maxValue - minValue) * NextDouble()));
    }

    public double NextDouble()
    {
        var data = new byte[sizeof(uint)];
        rng.GetBytes(data);
        var randUint = BitConverter.ToUInt32(data, 0);
        return randUint / (uint.MaxValue + 1.0);
    }

    public override void GetBytes(byte[] data)
    {
        rng.GetBytes(data);
    }

    public override void GetNonZeroBytes(byte[] data)
    {
        rng.GetNonZeroBytes(data);
    }
}
Run Code Online (Sandbox Code Playgroud)


0xD*_*EEF 5

我真的不建议使用提供的示例。虽然RNGCryptoServiceProvider返回真正好的随机(或至少应该),但对于Random. 此外 - 不知道是否Random(value)针对 返回的值创建真正的双射Next(...)。此外 - 不能保证Next(min, max)以真正随机的方式返回值(意味着数字命中每个值的机会相等)。

我首先将问题分解为在区间 0 - max(不包括)中获取一个数字。然后,我将使用最接近的 2 次幂来获得 0 - (2^n - 1) 范围内的随机值。现在,您在这里绝对不能做的一件事是使用模来获得首选范围内的数字,例如rand(0 - (2^n - 1)) % max,因为这样做实际上会增加获得较低范围内数字的机会。

示例:max = 3, n = 2 (0 - (2^2 - 1)) % 2, 数字 (0, 1, 2, 3),模数 (0, 1, 2, 0) 后的对应值。看到我们两次命中 0,这是非常糟糕的随机性。

因此,解决方案是使用加密随机数获取最接近 2 的幂的值,如果该值超出最大范围,则重复过程(获取另一个加密随机数)直到该值在给定范围内。这将是一个更好的算法。