Benzersiz rastgele dizi oluşturma


98

Örneğin, MSDN kitaplığı ( Hata Nesnesi ) tarafından oluşturulanlar gibi rastgele benzersiz dizeler oluşturmak istiyorum . 'T9zk6eay' gibi bir dize oluşturulmalıdır.


1
Bu deneyin string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);daha bulunabilir burada
shaijut

1
Bir şeyin tamamen benzersiz olması için zaman, konum vb. Gibi rastgele olmayan bir şeye dayanması gerekir ve bu nedenle hiçbir zaman tam olarak rastgele olamaz. Bir Kılavuz rastgele görünebilir, ancak gerçekte değildir. IMO'nun tek umudunuz onu o kadar rastgele ve karmaşık hale getirmektir ki, tüm pratik amaçlar için değerler benzersiz olacaktır (yani, son derece düşük bir çarpışma olasılığına sahip).
bytedev

Yanıtlar:


86

Guid'i kullanmak oldukça iyi bir yol olabilir, ancak örneğinize benzer bir şey elde etmek için, muhtemelen onu bir Base64 dizesine dönüştürmek istersiniz:

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

Örneğinize biraz daha yaklaşmak için "=" ve "+" karakterlerinden kurtuldum, aksi takdirde dizenizin sonunda "==" ve ortasında "+" elde edersiniz. İşte bir örnek çıktı dizesi:

"OZVV5TpP4U6wJthaCORZEQ"


15
/ Değiştirmeyi de düşünmelisiniz.
Jason Kealey

20
Bir Kılavuz, sıra tahmin edilebileceği için güvenli bir rastgele dizge olarak düşünülmemelidir. Bir Kılavuz, rastgele olmaktan ziyade önemli çatışmalardan kaçınmak için tasarlanmıştır. Yığın taşması etrafında bir Kılavuzun rastgeleliği hakkında bazı iyi tartışmalar vardır .
Daniel Bradley

Neyle ilgili Convert.ToBase64Stringolduğu konusunda net ve kısa açıklama için buraya bir göz atın .
jwaliszko

2
Guid'i base64'e dönüştürmek ve + ve = çarpışma olasılığını artırmak mı?
Milan Aggarwal

7
@SimonEjsing new Guid()"Hack" olmadan (saat veya dahili Windows veri yapılarını kurcalamak) kullanırken çarpışmalara neden olan bir uygulama yazabilirseniz sizi bir biraya davet edeceğim . İstediğiniz kadar çekirdek, iş parçacığı, senkronizasyon ilkelleri vb. Kullanmaktan çekinmeyin.
Lucero

175

2016/1/23 güncellemesi

Bu yanıtı yararlı bulursanız, yayınladığım basit (~ 500 SLOC) bir parola oluşturma kitaplığı ilginizi çekebilir :

Install-Package MlkPwgen

Ardından, aşağıdaki cevapta olduğu gibi rastgele dizeler oluşturabilirsiniz:

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

Kitaplığın bir avantajı, kodun daha iyi çarpanlara ayrılmasıdır, böylece dizeler oluşturmaktan daha fazlası için güvenli rastgeleliği kullanabilirsiniz . Check out proje sitesini fazla ayrıntı için.

Orijinal Cevap

Henüz kimse güvenli kod sağlamadığından, herhangi birinin yararlı bulması durumunda aşağıdakileri gönderiyorum.

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

.NET Core üzerinde kodun nasıl çalıştırılacağına işaret ettiği için Ahmad'a teşekkürler.


@Keltex çözümü meh için doğru çalışmıyordu (birkaç kullanımdan sonra aynı dizeyi döndürüyordu). Bu çözüm mükemmel çalışıyor :)
JoanComasFdz

2
@LeeGrissom, önyargı önemli bir unsurdur. Örneğin alfabenizin 255 karakter içerdiğini ve 0-255 arasında rastgele bir değer elde ettiğinizi varsayalım. Bir halka arabelleğinde hem 0 hem de 255 değeri, sonucu alfabedeki ilk karakter lehine çarpıtacak şekilde aynı karaktere karşılık gelir, daha az rasgele olur. bu konu elbette uygulamaya bağlıdır.
Oskar Sjöberg

4
Kim hedef alıyor .netcore: Değiştir var rng = new RNGCryptoServiceProvider()ilevar rng = RandomNumberGenerator.Create()
amd

1
Neden 'var outOfRangeStart = byteSize - (byteSize% allowCharSet.Length);' her yineleme için? Bunu 'kullanmadan' önce hesaplayabilirsiniz.
mtkachenko

1
@BartCalixto Düzeltildi. Teşekkürler!
Michael Kropat

38

GUID'lerin rastgele sayılar olmadığı konusunda uyarırım . Tamamen rastgele olmasını beklediğiniz herhangi bir şeyi oluşturmak için temel olarak kullanılmamalıdırlar (bkz. Http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

WinAPI GUID oluşturucusunun kriptanalizasyonu, V4 GUID'leri dizisinin sözde rasgele olduğundan, başlangıç ​​durumu göz önüne alındığında, UuidCreate işlevi tarafından döndürülen sonraki 250.000 GUID'e kadar tahmin edilebileceğini göstermektedir. Bu nedenle GUID'ler kriptografide rasgele anahtarlar olarak kullanılmamalıdır.

Bunun yerine, sadece C # Random yöntemini kullanın. Bunun gibi bir şey ( kod burada bulundu ):

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

Benzersiz bir şey istiyorsanız (benzersiz bir dosya adı veya bir veritabanındaki anahtar gibi) GUID'ler iyidir , ancak rastgele olmasını istediğiniz bir şey için (parola veya şifreleme anahtarı gibi) iyi değildirler. Bu yüzden uygulamanıza bağlıdır.

Düzenleyin . Microsoft, Random'ın da o kadar iyi olmadığını söylüyor ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Örneğin, rasgele bir parola oluşturmaya uygun şifreleme açısından güvenli bir rasgele sayı oluşturmak için, System.Security.Cryptography.RNGCryptoServiceProvider gibi System.Security.Cryptography.RandomNumberGenerator'dan türetilen bir sınıf kullanın.


5
C # rastgele sınıfı da "rastgele" değildir ve belirli bir tohum numarasından başlayan klasik bir rastgele oluşturucu olduğundan herhangi bir kripto kodu için uygun değildir. Aynı tohum, döndürülen aynı sayı dizisini de döndürecektir; GUID yaklaşımı burada zaten çok daha iyi durumda ("rastgele" değil, "benzersiz").
Lucero

3
@Lucero: Haklısın. Microsoft, "Rasgele bir parola oluşturmaya uygun şifreleme açısından güvenli bir rasgele sayı oluşturmak için, örneğin System.Security.Cryptography.RNGCryptoServiceProvider gibi System.Security.Cryptography.RandomNumberGenerator'dan türetilen bir sınıf kullanın."
Keltex

Soru zaten rastgele benzersiz diziler (sözde) istediğini, dolayısıyla kripto gereksinimi olmadığını ve hatta belirli bir rastgele dağıtımı takip etme ihtiyacı olduğunu belirtti. Dolayısıyla GUID muhtemelen en kolay yaklaşımdır.
Joey

1
"Başlangıç ​​durumu göz önüne alındığında, sonraki 250.000 GUID'e kadar öngörülebilir" ifadesi, herhangi bir PRNG için doğası gereği doğru bir ifade gibi görünüyor ... Eminim ki bu da güvenli değildir, ancak üretmenin çok değerli olduğundan emin değilim gerçekten rastgele URL'ler, eğer OP bunun için gidiyorsa. ;)
ojrac

1
(+1 yine de - PRNG eğitimi önemli.)
ojrac

13

@Michael Kropats çözümünü basitleştirdim ve LINQ-esque versiyonunu yaptım.

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

12

Gerçekten rastgele olduklarını sanmıyorum, ama benim tahminim bunlar bazı karmalar.

Ne zaman rastgele bir tanımlayıcıya ihtiyacım olsa, genellikle bir GUID kullanıyorum ve onu "çıplak" temsiline dönüştürüyorum:

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

@Keltex'in belirttiği gibi: WinAPI GUID oluşturucusunun kriptanalizasyonu, V4 GUID'leri dizisinin sözde rasgele olduğunu gösterdiğinden, başlangıç ​​durumu göz önüne alındığında, UuidCreate işlevi tarafından döndürülen sonraki 250.000 GUID'e kadar tahmin edilebileceğini göstermektedir.
JoanComasFdz

4

Guid ve Time arasındaki kombinasyonu deneyin.

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

3

Neden CrytpoGraphic çözümünün bulunmadığına şaşırdım. GUID benzersizdir ancak kriptografik olarak güvenli değildir . Bu Dotnet Fiddle'a bakın.

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

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

Bir Kılavuzun Başına Eklemek istemeniz durumunda:

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

Daha temiz bir alfanümerik dize:

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

1

VB.net'te Michael Kropats çözümü

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

Bu benim için mükemmel çalışıyor

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

0

Bu, çeşitli diller için istenmiştir. Burada da geçerli olması gereken şifreler hakkında bir soru var .

URL kısaltması için dizeleri kullanmak istiyorsanız, oluşturulan bir kimliğin zaten kullanılıp kullanılmadığını görmek için ayrıca bir Sözlük <> veya veritabanı kontrolüne ihtiyacınız olacaktır.


0

Küçük ve büyük harfli alfasayısal dizeler ([a-zA-Z0-9]) istiyorsanız, hızlı ve basit bir çözüm için Convert.ToBase64String () kullanabilirsiniz.

Benzersizliğe gelince, bir çarpışmanın ne kadar olası olduğunu hesaplamak için doğum günü problemini kontrol edin (A) oluşturulan dizelerin uzunluğu ve (B) oluşturulan dizelerin sayısı.

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
  • Microsoft'un bağlantısının rastgele oluşturulduğundan emin değilim
  • yeni Guid () 'e bir göz atın. ToString ()

4
Guid.NewGuid () demek istiyorsun. ToString ()
Guid'in

3
Muhtemelen haklısın, doğrulamadan yazıyordum. Eminim asıl posterin anlamı vardır.
Fabian Vilers

-1

GUID Hash kodunu kullanarak Benzersiz Anahtar alın

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

Bu mükemmel çalışır, ancak rastgele sözcükler benzersiz karakterler içermez. Karakterler, 114e3 (iki 1), eaaea (üç a ve iki e), 60207 (iki 0) ve benzeri gibi tekrar ediyor. Alfasayısal kombinasyon ile karakterlerin tekrarı olmadan Rastgele bir dize nasıl oluşturulur?
vijay

@vijay: Onaltılık rakamlar çıkardığı için, kendinizi 16 karakter ve 16 ile sınırlıyorsunuz! olası çıktılar. Rastgele dizeler tam da bu, rastgele. Teorik olarak tüm a'lardan (aaaaaaaaaaaaaaa) bir dizi elde edebilirsiniz. Bu çok olası değil, ancak diğer rastgele dizelerden daha fazlası değil. Bu kısıtlamaya neden ihtiyaç duyduğunuzdan emin değilim, ancak karakterleri dizeye eklerken, onları bir HashSet <T> içinde açın, varlıklarını kontrol edin ve dizeye ekleyin veya uygun şekilde atlayın.
Chris Doggett

-2

Windows'taki Linux komutları için tek durak çözümüm kepçe. Scoop.sh adresinden kepçe yükleyin

scoop install openssl
openssl rand -base64 32
Dca3c3pptVkcb8fx243wN/3f/rQxx/rWYL8y7rZrGrA=

1
Bu cevabın C # ile ilgisi olmadığı için reddedildi. Farklı bir soru üzerine daha iyi bir yuva bulabilir.
Peter O.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.