2011-02-03 33 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 
}