2011-02-03 16 views
19

Mientras buscaba los mejores intentos para generar números realmente aleatorios, tropecé con este ejemplo de código.RNGCryptoServiceProvider - Revisión de número aleatorio

Buscando opiniones en este fragmento.

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

Fuente: http://www.vcskicks.com/code-snippet/rng-int.php

¿Esto es preferible a usar una semilla recuento de tics tales como:

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

Nota:

Yo no busco a terceros proveedores de datos aleatorios como Random.org, ya que tal dependencia no es realista para la aplicación licación.

+2

No hay mejores prácticas para 'números aleatorios'. Solo para escenarios concretos que requieren números aleatorios. –

Respuesta

16

Bueno, el uso de RNGCryptoServiceProvider le da una semilla inimaginable cripto-fuerza, mientras que Environment.TickCount es, en teoría, predecible.

Otra diferencia crucial sería evidente al llamar al método NextInt varias veces en rápida sucesión. El uso de RNGCryptoServiceProvider sembrará el objeto Random con un número diferente de cifrado cada vez, lo que significa que continuará devolviendo un número aleatorio diferente para cada llamada. Usar TickCount corre el riesgo de sembrar el objeto Random con el mismo número cada vez (si el método se llama varias veces durante el mismo "tic"), lo que significa que continuará devolviendo el mismo número (supuestamente aleatorio) para cada llamada.

Si realmente necesita números verdaderos, entonces no debe utilizar una computadora para generarlos en absoluto: debe medir la descomposición radiactiva o algo similar, realmente impredecible.

+0

No es la semilla la que es tan vulnerable, pero tiene unos pocos valores pseudoaleatorios que hacen que sea factible 'adivinar' la siguiente. –

+2

@Henk: Es cierto, pero en el ejemplo del OP cada instancia 'Random' es de un solo uso, así que no creo que sea un problema. Sembrar el RNG con un número de cripto-fuerza; generar un solo entero; descartar el RNG; repita según sea necesario. No es una configuración ideal, pero debe ser razonablemente segura. (Por supuesto, si se requieren números de cripto-fuerza realmente, entonces el OP debería consultar a un experto en criptografía). – LukeH

+2

Apagado en una tangente aquí, pero seguramente la desintegración radiactiva es bastante predecible. – Jodrell

5

Le pregunté a similar question hace 2 años :) compruebe y vea si le ayuda. Utilicé ese código para generar un número aleatorio seguro para el procesamiento de pagos.

1

Realmente depende del uso previsto o requisito del número aleatorio que se genera.

La clase Random es útil para la aleatorización práctica, como aleatorizar la visualización de las imágenes del pedido en un rotador de imagen o rollos de un dado.
Si, por otro lado, necesita números aleatorios que requieren una mayor cantidad de seguridad, como generar una contraseña o clave de confirmación de pago, luego usar una clase como RNGCryptoServiceProvider o crear su propia implementación de la clase abstracta RandomNumberGenerator que implementa un algoritmo criptográfico son mejores alternativas.

1

Realmente no sugiero usar el ejemplo provisto. Aunque RNGCryptoServiceProvider devuelve un muy buen azar (o al menos debería), pero lo mismo no es cierto para Random. Además, no se sabe si Random (valor) crea una biyección verdadera contra el valor reajustado por Next (...). Además, no está garantizado que Next (min, max) devuelva valor en maner verdaderamente aleatorio (es decir, con la misma probabilidad de que el número llegue a cada valor).

Primero eliminaría el problema para obtener el número en el intervalo 0 - max (exclusivo). Entonces usaría la potencia más cercana a 2 para obtener un valor aleatorio en el rango 0 - (2^n - 1).Ahora, una cosa que nunca DEBE hacer aquí es usar módulo para obtener el número en el rango preferido como rand (0 - (2^n - 1))% max, porque al hacerlo realmente está aumentando las posibilidades de obtener el número en el rango inferior.

Ejemplo - max = 3, n = 2 (0 - (2^2 - 1))% 2, números (0, 1, 2, 3), valores correspondientes después del módulo (0, 1, 2, 0) Mira que golpeamos 0 dos veces, lo cual es realmente malo al azar.

Así que la solución sería usar cripto aleatorio para obtener el valor de la potencia más cercana a dos y en caso de que el valor esté fuera del rango máximo, repita proceudre (obtenga otra criptografía aleatoria) hasta que el valor esté dentro del rango. Esto sería mucho mejor algoritmo.

5

No utilice su código. Su solución es incorrecta y genera números aleatorios pobres. Sugiero mi solución, que genera criptográficamente fuertes números aleatorios:

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

Entonces la solución que ha proporcionado es buena o mala, ya que estoy confundido con su información. – Chorel

+0

Me gusta el concepto, pero la implementación tiene errores. Llamar a 'new SecureRandom() .Next (int.MinValue, int.MaxValue);' siempre devuelve el mismo valor. –

+0

@ Bob.at.SBS Fixed. –

2

Creo que este es un generador más eficiente, y posiblemente más rápido que las mencionadas anteriormente ..

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 
} 
1

Ok, así que' Llegué un poco tarde a la fiesta, pero realmente quería una implementación completa de System.Random que se pueda invocar varias veces durante el mismo temporizador y producir resultados diferentes. Después de mucho agonizando sobre diferentes implementaciones, que se establecieron en la más simple que se me ocurrió, que proporciona un constructor por defecto que suministra una clave aleatoria al constructor base de 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); 
    } 
} 

lo largo del camino, yo también escribió y probó una implementación que anula los métodos necesarios para usar RNGCryptoServiceProvider para proporcionar TODOS los valores aleatorios (en lugar de depender de cualquier generador de números aleatorios que esté integrado en la clase System.Random). Pero no tengo idea de cuán criptográficamente fuertes son los resultados cuando tomo mis valores aleatorios de Sample() y los empujo a través de las transformaciones para producir valores enteros. De todos modos, aquí está el código si alguien lo quiere:

/// <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 
} 
Cuestiones relacionadas