Tạo chuỗi ngẫu nhiên duy nhất


97

Tôi muốn tạo các chuỗi duy nhất ngẫu nhiên giống như các chuỗi được tạo bởi thư viện MSDN. ( Đối tượng Lỗi ) chẳng hạn. Một chuỗi như 't9zk6eay' sẽ được tạo.


1
thử này string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);hơn có thể được tìm thấy ở đây
shaijut

1
Đối với một cái gì đó là hoàn toàn độc nhất, nó phải dựa trên một cái gì đó không ngẫu nhiên, như thời gian, địa điểm, v.v. và do đó không bao giờ có thể thực sự là hoàn toàn ngẫu nhiên. Một hướng dẫn có vẻ ngẫu nhiên nhưng thực tế không phải vậy. IMO hy vọng duy nhất của bạn là làm cho nó ngẫu nhiên và phức tạp đến mức cho tất cả các mục đích thực tế, các giá trị sẽ là duy nhất (tức là có xác suất va chạm cực kỳ thấp).
bytedev

Câu trả lời:


84

Sử dụng Guid sẽ là một cách khá hay, nhưng để có được thứ gì đó giống như ví dụ của bạn, bạn có thể muốn chuyển nó thành chuỗi Base64:

    Guid g = Guid.NewGuid();
    string GuidString = Convert.ToBase64String(g.ToByteArray());
    GuidString = GuidString.Replace("=","");
    GuidString = GuidString.Replace("+","");

Tôi loại bỏ "=" và "+" để đến gần hơn một chút với ví dụ của bạn, nếu không, bạn nhận được "==" ở cuối chuỗi và dấu "+" ở giữa. Đây là một chuỗi đầu ra ví dụ:

"OZVV5TpP4U6wJthaCORZEQ"


15
Bạn nên xem xét việc thay thế / quá.
Jason Kealey

20
Hướng dẫn không nên được coi là một chuỗi ngẫu nhiên an toàn vì có thể đoán được chuỗi. Hướng dẫn được thiết kế để tránh xung đột chính, thay vì ngẫu nhiên. Có một số thảo luận tốt về tính ngẫu nhiên của Hướng dẫn xung quanh việc tràn ngăn xếp.
Daniel Bradley,

Để được giải thích rõ ràng và ngắn gọn về nội dung Convert.ToBase64String, hãy xem ở đây .
jwaliszko

2
Có thể chuyển đổi hướng dẫn thành base64 và thay thế + và = tăng xác suất va chạm không?
Milan Aggarwal

5
@SimonEjsing Tôi sẽ mời bạn một cốc bia nếu bạn thực sự có thể viết một ứng dụng bị va chạm khi sử dụng new Guid()mà không bị "hack" (giả mạo đồng hồ hoặc cấu trúc dữ liệu nội bộ của Windows). Hãy thoải mái sử dụng bao nhiêu lõi, luồng, nguyên thủy đồng bộ hóa, v.v. tùy thích.
Lucero

175

Cập nhật 2016/1/23

Nếu bạn thấy câu trả lời này hữu ích, bạn có thể quan tâm đến thư viện tạo mật khẩu đơn giản (~ 500 SLOC) mà tôi đã xuất bản :

Install-Package MlkPwgen

Sau đó, bạn có thể tạo các chuỗi ngẫu nhiên giống như trong câu trả lời bên dưới:

var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);

Một lợi thế của thư viện là mã được tính toán tốt hơn để bạn có thể sử dụng tính ngẫu nhiên an toàn cho nhiều hơn là tạo chuỗi . Kiểm tra trang web dự án để biết thêm chi tiết.

Câu trả lời gốc

Vì chưa có ai cung cấp mã bảo mật nên tôi đăng phần sau để phòng khi có ai thấy hữu ích.

string RandomString(int length, string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
    if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
    if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");

    const int byteSize = 0x100;
    var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
    if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));

    // Guid.NewGuid and System.Random are not particularly random. By using a
    // cryptographically-secure random number generator, the caller is always
    // protected, regardless of use.
    using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) {
        var result = new StringBuilder();
        var buf = new byte[128];
        while (result.Length < length) {
            rng.GetBytes(buf);
            for (var i = 0; i < buf.Length && result.Length < length; ++i) {
                // Divide the byte into allowedCharSet-sized groups. If the
                // random value falls into the last group and the last group is
                // too small to choose from the entire allowedCharSet, ignore
                // the value in order to avoid biasing the result.
                var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                if (outOfRangeStart <= buf[i]) continue;
                result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
            }
        }
        return result.ToString();
    }
}

Cảm ơn Ahmad đã chỉ ra cách để mã hoạt động trên .NET Core.


Giải pháp @Keltex không hoạt động phù hợp với meh (nó trả về cùng một chuỗi sau một vài lần sử dụng). Giải pháp này hoạt động :) hoàn hảo
JoanComasFdz

2
@LeeGrissom, xu hướng là một khía cạnh quan trọng. Ví dụ: giả sử bảng chữ cái của bạn chứa 255 ký tự và bạn nhận được một giá trị ngẫu nhiên trong khoảng 0-255. Trong bộ đệm vòng, cả giá trị 0 và 255 sẽ tương ứng với cùng một ký tự, điều này sẽ làm lệch kết quả có lợi cho ký tự đầu tiên trong bảng chữ cái, nó sẽ ít ngẫu nhiên hơn. nếu điều này quan trọng, tất nhiên phụ thuộc vào ứng dụng.
Oskar Sjöberg

4
Ai đang nhắm mục tiêu .netcore: Thay thế var rng = new RNGCryptoServiceProvider()bằngvar rng = RandomNumberGenerator.Create()
AMD

1
Tại sao bạn lại tính toán 'var outOfRangeStart = byteSize - (byteSize% allowCharSet.Length);' cho mỗi lần lặp? Bạn có thể tính toán nó trước khi 'sử dụng'.
mtkachenko

1
@BartCalixto Đã sửa. Cảm ơn!
Michael Kropat

38

Tôi xin lưu ý rằng các GUID không phảisố ngẫu nhiên . Chúng không nên được sử dụng làm cơ sở để tạo bất kỳ thứ gì mà bạn mong đợi là hoàn toàn ngẫu nhiên (xem http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

Phân tích mật mã của trình tạo GUID WinAPI cho thấy rằng, vì chuỗi các GUID của V4 là giả ngẫu nhiên, với trạng thái ban đầu, người ta có thể dự đoán lên đến 250 000 GUID tiếp theo được trả về bởi hàm UuidCreate. Đây là lý do tại sao GUID không nên được sử dụng trong mật mã, chẳng hạn như khóa ngẫu nhiên.

Thay vào đó, chỉ cần sử dụng phương pháp C # Random. Một cái gì đó như thế này ( mã được tìm thấy ở đây ):

private string RandomString(int size)
{
  StringBuilder builder = new StringBuilder();
  Random random = new Random();
  char ch ;
  for(int i=0; i<size; i++)
  {
    ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
    builder.Append(ch);
  }
  return builder.ToString();
}

GUID là tốt nếu bạn muốn một cái gì đó độc đáo (như một tên tệp hoặc khóa duy nhất trong cơ sở dữ liệu), nhưng chúng không tốt cho một cái gì đó bạn muốn ngẫu nhiên (như mật khẩu hoặc khóa mã hóa). Vì vậy, nó phụ thuộc vào ứng dụng của bạn.

Chỉnh sửa . Microsoft nói rằng Random cũng không phải là tuyệt vời ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Ví dụ, để tạo một số ngẫu nhiên an toàn bằng mật mã phù hợp để tạo mật khẩu ngẫu nhiên, hãy sử dụng một lớp có nguồn gốc từ System.Security.Cryptography.RandomNumberGenerator như System.Security.Cryptography.RNGCryptoServiceProvider.


5
Lớp ngẫu nhiên C # cũng không phải là "ngẫu nhiên" và không phù hợp với bất kỳ mã tiền điện tử nào, vì nó là một trình tạo ngẫu nhiên cổ điển bắt đầu từ một số hạt cụ thể. Giống nhau cũng sẽ trả về cùng một dãy số được trả về; cách tiếp cận GUID đã tốt hơn nhiều ở đây (không phải "ngẫu nhiên" mà là "duy nhất").
Lucero

3
@Lucero: Bạn nói đúng. Microsoft khuyến nghị, "Để tạo một số ngẫu nhiên an toàn bằng mật mã phù hợp để tạo mật khẩu ngẫu nhiên, ví dụ, hãy sử dụng một lớp bắt nguồn từ System.Security.Cryptography.RandomNumberGenerator như System.Security.Cryptography.RNGCryptoServiceProvider."
Keltex

Chà, câu hỏi đã nói rằng anh ta muốn (giả) các chuỗi duy nhất ngẫu nhiên, vì vậy không có yêu cầu về tiền điện tử hoặc thậm chí không cần tuân theo một phân phối ngẫu nhiên cụ thể. Vì vậy, GUID có lẽ là cách tiếp cận dễ dàng nhất.
Joey

1
Tuyên bố "với trạng thái ban đầu, người ta có thể dự đoán lên tới 250 000 GUID tiếp theo" có vẻ như là một tuyên bố vốn dĩ đúng cho bất kỳ PRNG nào ... Tôi chắc chắn rằng nó cũng không an toàn, nhưng tôi không chắc có nhiều giá trị trong việc tạo URL thực sự ngẫu nhiên, nếu đó là những gì OP đang làm. ;)
ojrac

1
(Dù sao thì +1 - giáo dục PRNG rất quan trọng.)
ojrac

13

Tôi đã đơn giản hóa giải pháp @Michael Kropats và tạo phiên bản LINQ-esque.

string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{       
    var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;

    return string.Concat(
        Enumerable
            .Repeat(0, int.MaxValue)
            .Select(e => RandomByte())
            .Where(randomByte => randomByte < outOfRange)
            .Take(length)
            .Select(randomByte => alphabet[randomByte % alphabet.Length])
    );
}

byte RandomByte()
{
    using (var randomizationProvider = new RNGCryptoServiceProvider())
    {
        var randomBytes = new byte[1];
        randomizationProvider.GetBytes(randomBytes);
        return randomBytes.Single();
    }   
}

11

Tôi không nghĩ rằng chúng thực sự là ngẫu nhiên, nhưng tôi đoán đó là một số hàm băm.

Bất cứ khi nào tôi cần một số nhận dạng ngẫu nhiên, tôi thường sử dụng GUID và chuyển đổi nó thành biểu diễn "trần trụi" của nó:

Guid.NewGuid().ToString("n");

Như @Keltex đã chỉ ra: Phân tích mã hóa của trình tạo GUID WinAPI cho thấy rằng, vì chuỗi các GUID của V4 là giả ngẫu nhiên, với trạng thái ban đầu, người ta có thể dự đoán lên đến 250 000 GUID tiếp theo do hàm UuidCreate trả về.
JoanComasFdz

4

Hãy thử kết hợp giữa Hướng dẫn và Thời gian.

 var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
     randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");

3

Tôi ngạc nhiên tại sao không có giải pháp CrytpoGraphic tại chỗ. GUID là duy nhất nhưng không an toàn về mặt mật mã . Xem Dotnet Fiddle này.

var bytes = new byte[40]; // byte size
using (var crypto = new RNGCryptoServiceProvider())
  crypto.GetBytes(bytes);

var base64 = Convert.ToBase64String(bytes);
Console.WriteLine(base64);

Trong trường hợp bạn muốn Thêm trước bằng Hướng dẫn:

var result = Guid.NewGuid().ToString("N") + base64;
Console.WriteLine(result);

Một chuỗi chữ và số rõ ràng hơn:

result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);

1

Giải pháp Michael Kropats trong VB.net

Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
    If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
    If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")


    Dim byteSize As Integer = 256
    Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
    'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
    Dim allowedCharSet() = hash.ToArray

    If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))


    ' Guid.NewGuid and System.Random are not particularly random. By using a
    ' cryptographically-secure random number generator, the caller is always
    ' protected, regardless of use.
    Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
    Dim result = New System.Text.StringBuilder()
    Dim buf = New Byte(128) {}
    While result.Length < length
        rng.GetBytes(buf)
        Dim i
        For i = 0 To buf.Length - 1 Step +1
            If result.Length >= length Then Exit For
            ' Divide the byte into allowedCharSet-sized groups. If the
            ' random value falls into the last group and the last group is
            ' too small to choose from the entire allowedCharSet, ignore
            ' the value in order to avoid biasing the result.
            Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
            If outOfRangeStart <= buf(i) Then
                Continue For
            End If
            result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
        Next
    End While
    Return result.ToString()
End Function

1

Điều này phù hợp với tôi

    private string GeneratePasswordResetToken()
    {
        string token = Guid.NewGuid().ToString();
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
        return Convert.ToBase64String(plainTextBytes);
    }

0

Điều này đã được yêu cầu cho các ngôn ngữ khác nhau. Đây là một câu hỏi về mật khẩu cũng nên được áp dụng ở đây.

Nếu bạn muốn sử dụng các chuỗi để rút ngắn URL, bạn cũng sẽ cần kiểm tra Từ điển <> hoặc cơ sở dữ liệu để xem liệu ID được tạo đã được sử dụng chưa.


0

Nếu bạn muốn một chuỗi chữ và số với các ký tự viết thường viết hoa ([a-zA-Z0-9]), bạn có thể sử dụng Convert.ToBase64String () để có một giải pháp nhanh chóng và đơn giản.

Đối với tính duy nhất, hãy kiểm tra bài toán ngày sinh để tính toán khả năng một collission được cung cấp (A) độ dài của các chuỗi được tạo và (B) số lượng chuỗi được tạo.

Random random = new Random();

int outputLength = 10;
int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
byte[] randomBytes = new byte[byteLength];
string output;
do
{
    random.NextBytes(randomBytes); // Fill bytes with random data
    output = Convert.ToBase64String(randomBytes); // Convert to base64
    output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
} while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)

-1
  • không chắc liên kết của Microsoft được tạo ngẫu nhiên
  • xem hướng dẫn mới (). ToString ()

4
Bạn có nghĩa Guid.NewGuid () ToString () -. Guid không có một constructor nào
CJK

3
Bạn hoàn toàn đúng, đang gõ w / o verfiying. Tôi chắc chắn rằng áp phích gốc có điểm.
Fabian Vilers

-1

Nhận Khóa duy nhất bằng mã Hash GUID

public static string GetUniqueKey(int length)
{
    string guidResult = string.Empty;

    while (guidResult.Length < length)
    {
        // Get the GUID.
        guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
    }

    // Make sure length is valid.
    if (length <= 0 || length > guidResult.Length)
        throw new ArgumentException("Length must be between 1 and " + guidResult.Length);

    // Return the first length bytes.
    return guidResult.Substring(0, length);
}

Điều này hoạt động hoàn hảo nhưng các từ ngẫu nhiên không chứa các ký tự duy nhất. Các ký tự đang lặp lại, như 114e3 (hai chữ 1), eaaea (ba chữ a và hai chữ e), 60207 (hai chữ 0), v.v. Làm thế nào để tạo một chuỗi Ngẫu nhiên không có sự lặp lại của các ký tự với tổ hợp chữ và số?
vijay

@vijay: Vì nó xuất ra các chữ số hex, bạn đang tự giới hạn mình ở 16 ký tự và 16! đầu ra có thể. Chuỗi ngẫu nhiên chỉ là, ngẫu nhiên. Về mặt lý thuyết, bạn có thể nhận được một chuỗi của tất cả các a (aaaaaaaaaaaaaaa). Nó rất không thể xảy ra, nhưng không nhiều hơn bất kỳ chuỗi ngẫu nhiên nào khác. Tôi không chắc tại sao bạn cần ràng buộc đó, nhưng khi bạn thêm các ký tự vào chuỗi, hãy đưa chúng vào HashSet <T>, kiểm tra sự tồn tại của chúng và thêm chúng vào chuỗi hoặc bỏ qua chúng cho phù hợp.
Chris Doggett
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.