2011-02-03 31 views
19

虽然寻找生成真正的随机数的最佳尝试,但我偶然发现了这个代码示例。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,因为这样的依赖对于应用来说并不现实lication。

+2

有没有'随机数'的最佳做法。仅适用于需要随机数的具体情况。 – 2011-02-03 22:47:23

回答

16

那么,使用RNGCryptoServiceProvider会给你一个难以猜测的密码强度种子,而理论上Environment.TickCount是可预测的。

当连续几次调用您的NextInt方法时,另一个重要的区别是显而易见的。使用RNGCryptoServiceProvider将每次对Random对象播种一个不同的加密强度号,这意味着它将继续为每个呼叫返回一个不同的随机数。使用TickCount风险每次都会使用相同的号码播种Random对象(如果在同一个“打勾”期间多次调用该方法),这意味着它将继续为每个呼叫返回相同的(假定为随机的)号码。

如果你真的需要真的随机数,那么你不应该使用电脑来产生它们:你应该测量放射性衰变或类似的东西,真正无法预测。

+0

这不是种子非常脆弱,但有一些伪随机值,可以“猜测”下一个。 – 2011-02-03 23:02:21

+2

@亨克:没错,但在OP的例子中,每个“随机”实例都是一次性的,所以我不认为这是个问题。用密码强度编号给RNG播种;生成一个整数;丢弃RNG;根据需要重复。这不是一个理想的设置,但应该相当安全。 (当然,如果需要真正的加密强度的数字,则OP应请教专家加密) – LukeH 2011-02-03 23:07:57

5

我问了一个similar question 2年回来:)检查并看看它是否可以帮助你。我使用该代码来生成用于付款处理的安全随机数。

1

这实际上取决于正在生成的随机数的预期用途或要求。

Random类对于实际随机化很有用,例如随机化图像旋转器中的顺序图像显示或模具卷。
另一方面,如果您需要随机数字,需要更大的安全性,例如生成密码或付款确认密钥,则使用类如RNGCryptoServiceProvider或创建您自己的实现抽象类RandomNumberGenerator,实现密码算法是更好的选择。

1

我真的不建议使用提供的示例。尽管RNGCryptoServiceProvider返回真正好的随机(或者至少应该),但对于Random却不是这样。更多 - 不知道随机(值)是否创建真正的双射对由Next(..)返回的值。更重要的是,不能保证Next(min,max)以真正随机的方式返回值(意味着数字达到每个值的机会相等)。

我会首先拆除问题,以获得间隔0 - 最大(独家)号码。然后,我将使用2的最近幂来得到范围0 - (2^n - 1)中的随机值。现在有一件事你必须永远不会做这里使用模获得数首选范围像兰特(0 - (2^N - 1))%以下,因为这样做你actualy增加较低的范围内歌厅数量的机会。 (0,1,2,0)之后的对应值(0,1,2,0),数字(0,1,2,3),以及对应的数值(0,1,2,0) )。看到我们打了0次两次,这是真的很糟糕的随机。

因此,解决办法是使用加密随机获得价值的两个最接近的功率和情况下,如果值超出了最大范围,重复proceudre(得到另一个密码是随机的),直到值内给定的范围。这将是更好的algorythm。

5

不要用你的代码。你的解决方案是错误的,并产生可怜的随机数我建议我的解决方案,它生成保密性强的随机数:

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

我觉得这是一种更有效,并可能更快发电机然后上面列出的..

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

好了,所以我有点迟到派对,但我真的想要一个完整的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'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 
}