it-roy-ru.com

RNGCryptoServiceProvider - Обзор случайных чисел

В поисках лучших попыток создания действительно случайных чисел я наткнулся на этот пример кода. 

Ищете мнения по этому фрагменту.

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);
}

Источник: http://www.vcskicks.com/code-snippet/rng-int.php

Будет ли это предпочтительным по сравнению с использованием семян подсчета тиков, таких как:

Random Rand = new Random(Environment.TickCount); 
Rand.Next(min, max);

Заметка:

Я не ищу сторонних поставщиков случайных данных, таких как Random.org , так как такая зависимость нереальна для приложения.

22
cweston

Что ж, использование RNGCryptoServiceProvider дает вам неоправданное начальное значение криптостойкости, тогда как Environment.TickCount теоретически предсказуемо.

Еще одно существенное отличие будет очевидно при вызове метода NextInt несколько раз подряд. Использование RNGCryptoServiceProvider приведет к тому, что объект Random будет начинать каждый раз с разных значений криптостойкости, что будет означать, что он будет возвращать разные случайные числа для каждого вызова. Использование TickCount рискует заполнить объект Random одним и тем же номером каждый раз (если метод вызывается несколько раз в течение одного и того же «тика»), что означает, что он будет возвращать один и тот же (предположительно случайный) номер для каждого вызова.

Если вам действительно нужны действительно случайные числа, тогда вам вообще не нужно использовать компьютер для их генерации: вам следует измерять радиоактивный распад или что-то подобное, действительно непредсказуемое.

18
LukeH

Не используйте свой код. Ваше решение неверно и генерирует плохие случайные числа. Я предлагаю свое решение, которое генерирует криптографически сильные случайные числа:

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);
    }
}
5
Eldar Agalarov

Я задал похожий вопрос 2 года назад :) проверь и посмотри, поможет ли это тебе. Я использовал этот код для генерации безопасного случайного числа для обработки платежей. 

5
Shoban

Я думаю, что это более эффективный и, возможно, более быстрый генератор, чем перечисленные выше.

public static class SecureRandom
{
    #region Constants
    private const int INT_SIZE = 4;
    private const int INT64_SIZE = 8;
    #endregion

    #region Fields
    private static RandomNumberGenerator _Random;
    #endregion

    #region Constructor
    static SecureRandom()
    {
        _Random = new RNGCryptoServiceProvider();
    }
    #endregion

    #region Random Int32
    /// <summary>
    /// Get the next random integer
    /// </summary>
    /// <returns>Random [Int32]</returns>
    public static Int32 Next()
    {
        byte[] data = new byte[INT_SIZE];
        Int32[] result = new Int32[1];

        _Random.GetBytes(data);
        Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);

        return result[0];
    }

    /// <summary>
    /// Get the next random integer to a maximum value
    /// </summary>
    /// <param name="MaxValue">Maximum value</param>
    /// <returns>Random [Int32]</returns>
    public static Int32 Next(Int32 MaxValue)
    {
        Int32 result = 0;

        do
        {
            result = Next();
        } while (result > MaxValue);

        return result;
    }
    #endregion

    #region Random UInt32
    /// <summary>
    /// Get the next random unsigned integer
    /// </summary>
    /// <returns>Random [UInt32]</returns>
    public static UInt32 NextUInt()
    {
        byte[] data = new byte[INT_SIZE];
        Int32[] result = new Int32[1];

        do
        {
            _Random.GetBytes(data);
            Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);
        } while (result[0] < 0);

        return (UInt32)result[0];
    }

    /// <summary>
    /// Get the next random unsigned integer to a maximum value
    /// </summary>
    /// <param name="MaxValue">Maximum value</param>
    /// <returns>Random [UInt32]</returns>
    public static UInt32 NextUInt(UInt32 MaxValue)
    {
        UInt32 result = 0;

        do
        {
            result = NextUInt();
        } while (result > MaxValue);

        return result;
    }
    #endregion

    #region Random Int64
    /// <summary>
    /// Get the next random integer
    /// </summary>
    /// <returns>Random [Int32]</returns>
    public static Int64 NextLong()
    {
        byte[] data = new byte[INT64_SIZE];
        Int64[] result = new Int64[1];

        _Random.GetBytes(data);
        Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);

        return result[0];
    }

    /// <summary>
    /// Get the next random unsigned long to a maximum value
    /// </summary>
    /// <param name="MaxValue">Maximum value</param>
    /// <returns>Random [UInt64]</returns>
    public static Int64 NextLong(Int64 MaxValue)
    {
        Int64 result = 0;

        do
        {
            result = NextLong();
        } while (result > MaxValue);

        return result;
    }
    #endregion

    #region Random UInt32
    /// <summary>
    /// Get the next random unsigned long
    /// </summary>
    /// <returns>Random [UInt64]</returns>
    public static UInt64 NextULong()
    {
        byte[] data = new byte[INT64_SIZE];
        Int64[] result = new Int64[1];

        do
        {
            _Random.GetBytes(data);
            Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);
        } while (result[0] < 0);

        return (UInt64)result[0];
    }

    /// <summary>
    /// Get the next random unsigned long to a maximum value
    /// </summary>
    /// <param name="MaxValue">Maximum value</param>
    /// <returns>Random [UInt64]</returns>
    public static UInt64 NextULong(UInt64 MaxValue)
    {
        UInt64 result = 0;

        do
        {
            result = NextULong();
        } while (result > MaxValue);

        return result;
    }
    #endregion

    #region Random Bytes
    /// <summary>
    /// Get random bytes
    /// </summary>
    /// <param name="data">Random [byte array]</param>
    public static byte[] NextBytes(long Size)
    {
        byte[] data = new byte[Size];
        _Random.GetBytes(data);
        return data;
    }
    #endregion
}
2
JGU

Я действительно не предлагаю использовать предоставленный пример. Хотя RNGCryptoServiceProvider возвращает действительно хорошее случайное число (или, по крайней мере, так должно быть), но то же самое не верно для случайного числа. Более того - неизвестно, создает ли Random (значение) истинную биекцию против значения, настроенного на Next (..). Более того - не гарантируется, что Next (min, max) возвращает значение в действительно случайном порядке (что означает равные шансы для числа достичь каждого значения).

Я хотел бы сначала разобрать проблему с получением числа в интервале 0 - макс (эксклюзив). Тогда я использовал бы ближайшую степень 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
0xDEAD BEEF

Это действительно зависит от предполагаемого использования или требования генерируемого случайного числа. 

Класс Random полезен для практической рандомизации, такой как рандомизация отображения изображений порядка в ротаторе изображений или бросках матрицы.
Если, с другой стороны, вам нужны случайные числа, требующие большей безопасности, например, для генерации пароля или ключа подтверждения платежа, то с использованием класса, такого как RNGCryptoServiceProvider или создание собственной реализации абстрактного class RandomNumberGenerator , который реализует криптографический алгоритм, являются лучшей альтернативой.

1
nybbler

Итак, я немного опоздал на вечеринку, но я действительно хотел полную реализацию System.Random, которая может вызываться несколько раз в течение одного таймера и давать разные результаты. После долгих мучений по поводу различных реализаций я остановился на простейшей из предложенных мной, которая предоставляет конструктор по умолчанию, который предоставляет случайный ключ для базового конструктора System.Random:

/// <summary> An implementation of System.Random whose default constructor uses a random seed value rather than the system time. </summary>
public class RandomEx : Random
{
    /// <summary> Initializes a new CryptoRandom instance using a random seed value. </summary>
    public RandomEx()
        : base(_GetSeed())
    { }

    /// <summary> Initializes a new CryptoRandom instance using the specified seed value. </summary>
    /// <param name="seed"> The seed value. </param>
    public RandomEx(int seed)
        : base(seed)
    { }

    // The static (shared by all callers!) RandomNumberGenerator instance
    private static RandomNumberGenerator _rng = null;

    /// <summary> Static method that returns a random integer. </summary>
    private static int _GetSeed()
    {
        var seed = new byte[sizeof(int)];

        lock (typeof(RandomEx)) {
            // Initialize the RandomNumberGenerator instance if necessary
            if (_rng == null) _rng = new RNGCryptoServiceProvider();

            // Get the random bytes
            _rng.GetBytes(seed);
        }

        // Convert the bytes to an int
        return BitConverter.ToInt32(seed, 0);
    }
}

Попутно я также написал и протестировал реализацию, которая переопределяет методы, необходимые для использования RNGCryptoServiceProvider для предоставления ВСЕХ случайных значений (вместо того, чтобы полагаться на какой-либо генератор случайных чисел, выпекаемый в классе System.Random). Но я понятия не имею, насколько криптографически сильны результаты к тому времени, когда вы берете мои случайные значения Sample () и проталкиваете их через преобразования, чтобы получить целочисленные значения. Во всяком случае, вот код, если кто-то хочет:

/// <summary> An implementation of System.Random that uses RNGCryptoServiceProvider to provide random values. </summary>
public class CryptoRandom : Random, IDisposable
{
    // Class data
    RandomNumberGenerator _csp = new RNGCryptoServiceProvider();

    /// <summary> Returns a random number between 0.0 (inclusive) and 1.0 (exclusive). </summary>
    protected override double Sample()
    {
        // Get a nonnegative random Int64
        byte[] bytes = new byte[sizeof(long)];
        _csp.GetBytes(bytes);
        long value = BitConverter.ToInt64(bytes, 0) & long.MaxValue;

        // Scale it to 0->1
        return (double)value / (((double)Int64.MaxValue) + 1025.0d);
    }

    /// <summary> Fills the elements of the specified array of bytes with random numbers. </summary>
    /// <param name="buffer"> An array of bytes to contain random numbers. </param>
    public override void NextBytes(byte[] buffer)
    {
        _csp.GetBytes(buffer);
    }

    /// <summary> Returns a nonnegative random integer. </summary>
    /// <returns> A 32-bit signed integer greater than or equal to zero. </returns>
    public override int Next()
    {
        byte[] data = new byte[4];
        _csp.GetBytes(data);
        data[3] &= 0x7f;
        return BitConverter.ToInt32(data, 0);
    }

    /// <summary> Returns a random integer that is within a specified range. </summary>
    /// <param name="minValue"> The inclusive lower bound of the random number returned. </param>
    /// <param name="maxValue"> The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. </param>
    /// <returns> A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned. </returns>
    public override int Next(int minValue, int maxValue)
    {
        // Special case
        if (minValue == maxValue) return minValue;

        double sample = Sample();
        double range = (double)maxValue - (double)minValue;
        return (int)((sample * (double)range) + (double)minValue);
    }

    #region IDisposible implementation

    /// <summary> Disposes the CryptoRandom instance and all of its allocated resources. </summary>
    public void Dispose()
    {
        // Do the actual work
        Dispose(true);

        // This object will be cleaned up by the Dispose method. Call GC.SupressFinalize to 
        // take this object off the finalization queue and prevent finalization code for this object 
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios:
    //
    // If disposing is true, the method has been called directly or indirectly by a user's code and both
    // managed and unmanaged resources can be disposed. 
    //
    // If disposing is false, the method has been called by the runtime from inside the finalizer.
    // In this case, only unmanaged resources can be disposed. 
    protected virtual void Dispose(bool disposing)
    {
        if (disposing) {
            // The method has been called directly or indirectly by a user's code; dispose managed resources (if any)
            if (_csp != null) {
                _csp.Dispose();
                _csp = null;
            }

            // Dispose unmanaged resources (if any)
        }
    }

    #endregion
}
1
Bob.at.Indigo.Health