Подтвердить что ты не робот

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, поскольку такая зависимость не является реалистичной для приложения.

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

Я действительно не предлагаю использовать предоставленный пример. Хотя RNGCryptoServiceProvider возвращает действительно хорошее случайное число (или, по крайней мере, так должно быть), но то же самое не верно для Random. Более того - неизвестно, если Random(value) создает истинную биекцию против значения, возвращаемого 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, что является действительно плохой случайностью.

Таким образом, решением было бы использовать криптослучайное значение, чтобы получить значение с ближайшей степенью двойки, а в случае, если значение находится за пределами максимального диапазона, повторять процедуру (получить еще один криптослучайный) до тех пор, пока значение не окажется внутри заданного диапазона. Это был бы намного лучший алгоритм.

Ответ 5

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

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
}

Ответ 6

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

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

Ответ 7

Хорошо, поэтому я немного опаздываю на вечеринку, но мне действительно нужна полная реализация System.Random, которую можно вызывать несколько раз в течение одного таймера tic и давать разные результаты. После многого мучения над различными реализациями я остановился на самом простом, с которым я столкнулся, который предоставляет конструктор по умолчанию, который поставляет случайный ключ в базовый конструктор 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 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 code; dispose managed resources (if any)
            if (_csp != null) {
                _csp.Dispose();
                _csp = null;
            }

            // Dispose unmanaged resources (if any)
        }
    }

    #endregion
}