﻿# 亂數產生器：Random 與 RNGCryptoServiceProvider

一般眾所周知的亂數的產生應該都會使用 [Random](https://learn.microsoft.com/zh-tw/dotnet/api/system.random) 類別，而在大部分的情境中使用 Random 類別其實是足夠用的，例如說你想要透過亂數選取目錄中的照片輸出到網頁中，或是將篩選出來的資料亂數排序後輸出到檔案（例如說抽獎程式）。

假設你要用 Random 類別產生 10 組最大值小於 100 的亂數（0 ~ 99），可以用以下寫法：

```csharp
Random rnd = new Random();
for (int i = 0; i < 10; i++)
{
   int randomNumber = rnd.Next(100);
}
```

詳細的說明與範例可參照 MSDN 文件：[Random](https://learn.microsoft.com/zh-tw/dotnet/api/system.random) 類別。

不過 Random 類別是使用「有限性數學演算法」所計算出來的結果（目前 Random 類別的實作是以 Donald E. Knuth 的減法亂數產生器演算法為主），因此計算出來的「亂數」其實不是真的那麼亂，若要將亂數套用在「密碼產生器」或用來產生「強勢金鑰(Strong Key)」的話，就應該使用 [System.Security.Cryptography.RNGCryptoServiceProvider](https://learn.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.rngcryptoserviceprovider) 類別幫你產生「夠強」的亂數。

如果要使用 RNGCryptoServiceProvider 類別產生亂數的話可以參考 MSDN 上的範例，或是用我之前常用的一個靜態類別來產生亂數（介面跟 Random 類別類似），如下：

```csharp
using System;
using System.Security.Cryptography;

/// <summary>
/// 使用 RNGCryptoServiceProvider 產生由密碼編譯服務供應者 (CSP) 提供的亂數產生器。
/// </summary>
public static class RNG
{
    private static RNGCryptoServiceProvider rngp = new RNGCryptoServiceProvider();
    private static byte[] rb = new byte[4];

    /// <summary>
    /// 產生一個非負數的亂數
    /// </summary>
    public static int Next()
    {
        rngp.GetBytes(rb);
        int value = BitConverter.ToInt32(rb, 0);
        if (value < 0) value = -value;
        return value;
    }
    /// <summary>
    /// 產生一個非負數且最大值 max 以下的亂數
    /// </summary>
    /// <param name="max">最大值</param>
    public static int Next(int max)
    {
        rngp.GetBytes(rb);
        int value = BitConverter.ToInt32(rb, 0);
        value = value % (max + 1);
        if (value < 0) value = -value;
        return value;
    }
    /// <summary>
    /// 產生一個非負數且最小值在 min 以上最大值在 max 以下的亂數
    /// </summary>
    /// <param name="min">最小值</param>
    /// <param name="max">最大值</param>
    public static int Next(int min, int max)
    {
        int value = Next(max - min) + min;
        return value;
    }
}
```

有個這個「夠強」的亂數產生器，就可以用來產生密碼了，如果你要產生一組固定長度 8 碼的密碼，可以用以下程式碼：

```csharp
System.Text.StringBuilder sb = new System.Text.StringBuilder();
char[] chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
int length = RNG.Next(8, 8);
for (int i = 0; i < length; i++)
{
    sb.Append(chars[RNG.Next(chars.Length - 1)]);
}
string YourPassword = sb.ToString();
```

這樣的密碼並沒有說就比較安全，只能說在演算法的角度來看是比較亂的密碼。如果你只是想要一串「亂亂的文字」的話，我也看過有人直接用 Guid 來當作亂碼。

```csharp
string RandomString = Guid.NewGuid().ToString();
```

### 相關連結

-   [RNGCryptoServiceProvider 類別](https://learn.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.rngcryptoserviceprovider)
-   [Random 類別](https://learn.microsoft.com/zh-tw/dotnet/api/system.random)
-   [Creating Random Numbers With .NET Core | Khalid Abuhakmeh](https://khalidabuhakmeh.com/creating-random-numbers-with-dotnet-core)
