Pembuatan string acak yang unik

97

Saya ingin membuat string unik acak seperti yang dibuat oleh perpustakaan MSDN. ( Objek Kesalahan ), misalnya. String seperti 't9zk6eay' harus dibuat.

Kirtan
sumber
1
coba ini string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);lebih dapat ditemukan di sini
shaijut
1
Agar sesuatu menjadi benar-benar unik, itu harus didasarkan pada sesuatu yang tidak acak, seperti waktu, lokasi, dll. Dan oleh karena itu, tidak pernah bisa benar-benar acak sepenuhnya. A Guid mungkin tampak acak, tetapi kenyataannya tidak. IMO satu-satunya harapan Anda adalah membuatnya begitu acak dan kompleks sehingga untuk semua tujuan praktis nilainya akan unik (yaitu memiliki probabilitas tabrakan yang sangat rendah).
bytedev

Jawaban:

84

Menggunakan Guid akan menjadi cara yang cukup bagus, tetapi untuk mendapatkan sesuatu yang tampak seperti contoh Anda, Anda mungkin ingin mengubahnya menjadi string Base64:

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

Saya menyingkirkan "=" dan "+" untuk lebih mendekati contoh Anda, jika tidak, Anda mendapatkan "==" di akhir string dan "+" di tengah. Berikut contoh string keluaran:

"OZVV5TpP4U6wJthaCORZEQ"

Tandai Synowiec
sumber
15
Anda harus mempertimbangkan untuk mengganti / juga.
Jason Kealey
20
Panduan tidak boleh dianggap sebagai string acak yang aman karena urutannya dapat ditebak. Panduan dirancang untuk menghindari konflik kunci, bukan acak. Ada beberapa diskusi bagus tentang keacakan Panduan di sekitar tumpukan overflow.
Daniel Bradley
Untuk penjelasan yang jelas dan singkat tentang apa Convert.ToBase64Stringitu, lihat di sini .
jwaliszko
2
Bisakah mengubah panduan menjadi base64 dan mengganti + dan = meningkatkan probabilitas tabrakan?
Milan Aggarwal
5
@SimonEjsing Saya akan mengundang Anda untuk minum bir jika Anda benar-benar dapat menulis aplikasi yang mengalami benturan saat menggunakan new Guid()tanpa "peretasan" (merusak jam atau struktur data internal Windows). Jangan ragu untuk menggunakan core, thread, primitif sinkronisasi, dll. Sesuka Anda.
Lucero
175

Perbarui 2016/1/23

Jika Anda merasa jawaban ini berguna, Anda mungkin tertarik dengan pustaka pembuatan kata sandi sederhana (~ 500 SLOC) yang saya terbitkan :

Install-Package MlkPwgen

Kemudian Anda dapat membuat string acak seperti pada jawaban di bawah ini:

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

Satu keuntungan dari pustaka adalah bahwa kodenya lebih baik difaktorkan sehingga Anda dapat menggunakan keacakan aman untuk lebih dari sekadar menghasilkan string . Lihat situs proyek untuk lebih jelasnya.

Jawaban Asli

Karena belum ada yang memberikan kode aman, saya memposting yang berikut ini jika ada yang menganggapnya berguna.

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

Terima kasih kepada Ahmad karena telah menunjukkan cara menjalankan kode pada .NET Core.

Michael Kropat
sumber
Solusi @Kex tidak berfungsi dengan benar untuk me (mengembalikan string yang sama setelah beberapa penggunaan). Solusi ini bekerja dengan sempurna :)
JoanComasFdz
2
@LeeGrissom, bias adalah aspek penting. Katakanlah misalnya alfabet Anda berisi 255 karakter dan Anda mendapatkan nilai acak antara 0-255. Dalam buffer cincin, baik nilai 0 dan 255 akan sesuai dengan karakter yang sama yang akan memiringkan hasilnya ke karakter pertama dalam alfabet, itu akan menjadi kurang acak. jika hal ini tergantung pada aplikasinya tentunya.
Oskar Sjöberg
4
Siapa yang menargetkan .netcore: Ganti var rng = new RNGCryptoServiceProvider()denganvar rng = RandomNumberGenerator.Create()
amd
1
Mengapa Anda menghitung 'var outOfRangeStart = byteSize - (byteSize% AllowedCharSet.Length);' untuk setiap iterasi? Anda dapat menghitungnya sebelum 'menggunakan'.
mtkachenko
1
@Tokopedia Terima kasih!
Michael Kropat
38

Saya akan mengingatkan bahwa GUID bukanlah nomor acak . Mereka tidak boleh digunakan sebagai dasar untuk menghasilkan apa pun yang Anda harapkan benar-benar acak (lihat http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

Analisis kriptografi generator GUID WinAPI menunjukkan bahwa, karena urutan GUID V4 bersifat pseudo-random, mengingat status awal seseorang dapat memprediksi hingga 250.000 GUID berikutnya yang dikembalikan oleh fungsi UuidCreate. Inilah mengapa GUID tidak boleh digunakan dalam kriptografi, misalnya sebagai kunci acak.

Sebagai gantinya, cukup gunakan metode C # Random. Sesuatu seperti ini ( kode ditemukan di sini ):

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 tidak masalah jika Anda menginginkan sesuatu yang unik (seperti nama file atau kunci unik dalam database), tetapi GUID tidak baik untuk sesuatu yang Anda inginkan secara acak (seperti kata sandi atau kunci enkripsi). Jadi itu tergantung aplikasi Anda.

Edit . Microsoft mengatakan bahwa Random juga tidak terlalu bagus ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Untuk menghasilkan nomor acak yang aman secara kriptografis yang cocok untuk membuat kata sandi acak, misalnya, gunakan kelas yang diturunkan dari System.Security.Cryptography.RandomNumberGenerator seperti System.Security.Cryptography.RNGCryptoServiceProvider.

Keltex
sumber
5
Kelas acak C # juga tidak "acak" dan tidak cocok untuk kode kripto apa pun, karena ini adalah generator acak klasik yang dimulai dari nomor benih tertentu. Benih yang sama juga akan mengembalikan urutan nomor yang sama yang dikembalikan; pendekatan GUID sudah jauh lebih baik di sini (bukan "acak" tetapi "unik").
Lucero
3
@ Lucero: Anda benar. Microsoft menganjurkan, "Untuk menghasilkan nomor acak yang aman secara kriptografis yang sesuai untuk membuat sandi acak, misalnya, gunakan kelas yang diturunkan dari System.Security.Cryptography.RandomNumberGenerator seperti System.Security.Cryptography.RNGCryptoServiceProvider."
Keltex
Nah, pertanyaannya sudah menyatakan bahwa dia menginginkan (pseudo-) string unik acak, jadi tidak ada persyaratan crypto atau bahkan kebutuhan untuk mengikuti distribusi acak tertentu. Jadi GUID mungkin adalah pendekatan termudah.
Joey
1
Pernyataan bahwa "dengan keadaan awal seseorang dapat memprediksi hingga 250.000 GUID berikutnya" tampak seperti pernyataan yang secara inheren benar untuk setiap PRNG ... Saya yakin ini juga tidak aman, tetapi saya tidak yakin ada banyak manfaat dalam menghasilkan URL yang benar-benar acak, jika itu yang akan dilakukan OP. ;)
ojrac
1
(+1 bagaimanapun - pendidikan PRNG itu penting.)
ojrac
13

Saya menyederhanakan solusi @Michael Kropats dan membuat versi 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();
    }   
}
Oskar Sjöberg
sumber
11

Saya tidak berpikir bahwa mereka benar-benar acak, tetapi tebakan saya adalah beberapa hash.

Setiap kali saya membutuhkan pengenal acak, saya biasanya menggunakan GUID dan mengubahnya menjadi representasi "telanjang":

Guid.NewGuid().ToString("n");
Lucero
sumber
Seperti yang ditunjukkan oleh @Keltex: Analisis kriptografi generator GUID WinAPI menunjukkan bahwa, karena urutan GUID V4 bersifat pseudo-random, dengan keadaan awal seseorang dapat memprediksi hingga 250.000 GUID berikutnya yang dikembalikan oleh fungsi UuidCreate.
JoanComasFdz
4

Coba kombinasi antara Guid dan Time.Ticks

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

Saya terkejut mengapa tidak ada solusi CrytpoGraphic. GUID itu unik tetapi tidak aman secara kriptografis . Lihat Biola Dotnet ini.

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

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

Jika Anda ingin menambahkan dengan Panduan:

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

String alfanumerik yang lebih bersih:

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

Solusi Michael Kropats di 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
jhersey29
sumber
1

Ini bekerja sempurna untuk saya

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

Ini telah ditanyakan dalam berbagai bahasa. Inilah satu pertanyaan tentang kata sandi yang harus diterapkan di sini juga.

Jika Anda ingin menggunakan string untuk memperpendek URL, Anda juga memerlukan Dictionary <> atau pemeriksaan basis data untuk melihat apakah ID yang dihasilkan telah digunakan.

Pontus Gagge
sumber
0

Jika Anda menginginkan string alfanumerik dengan karakter huruf kecil dan huruf besar ([a-zA-Z0-9]), Anda dapat menggunakan Convert.ToBase64String () untuk solusi yang cepat dan sederhana.

Untuk keunikan, periksa masalah birthday untuk menghitung seberapa besar kemungkinan tumbukan diberikan (A) panjang string yang dihasilkan dan (B) jumlah string yang dihasilkan.

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)
Timo
sumber
-1
  • tidak yakin tautan Microsoft dibuat secara acak
  • lihat Guid () baru. ToString ()
Fabian Vilers
sumber
4
Maksud Anda Guid.NewGuid (). ToString () - Guid tidak memiliki konstruktor publik
cjk
3
Anda mungkin benar, sedang mengetik tanpa memverifikasi. Saya yakin poster asli benar.
Fabian Vilers
-1

Dapatkan Kunci Unik menggunakan kode 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);
}
Chris Doggett
sumber
Ini berfungsi dengan sempurna tetapi kata-kata acak tidak mengandung karakter unik. Karakter berulang, seperti 114e3 (dua 1), eaaea (tiga a dan dua e), 60207 (dua 0), dan seterusnya. Bagaimana cara menghasilkan string acak tanpa pengulangan karakter dengan kombinasi alfanumerik?
vijay
@vijay: Karena mengeluarkan digit hex, Anda membatasi diri Anda menjadi 16 karakter, dan 16! kemungkinan keluaran. String acak hanyalah itu, acak. Anda secara teoritis bisa mendapatkan string semua a (aaaaaaaaaaaaaaa). Ini sangat tidak mungkin, tetapi tidak lebih dari string acak lainnya. Saya tidak yakin mengapa Anda memerlukan batasan itu, tetapi saat Anda menambahkan karakter ke string, masukkan mereka ke dalam HashSet <T>, periksa keberadaannya, dan tambahkan ke string atau lewati sesuai.
Chris Doggett