Einzigartige zufällige Zeichenfolgengenerierung

97

Ich möchte zufällige eindeutige Zeichenfolgen generieren, wie sie beispielsweise von der MSDN-Bibliothek ( Fehlerobjekt ) generiert werden . Ein String wie 't9zk6eay' sollte generiert werden.

Kirtan
quelle
1
Versuchen Sie dies string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);mehr finden Sie hier
Shaijut
1
Damit etwas völlig einzigartig ist, muss es auf etwas basieren, das nicht zufällig ist, wie Zeit, Ort usw., und kann daher niemals vollständig zufällig sein. Ein Guid mag zufällig erscheinen, ist es aber in Wirklichkeit nicht. IMO besteht Ihre einzige Hoffnung darin, es so zufällig und komplex zu machen, dass die Werte für alle praktischen Zwecke eindeutig sind (dh eine äußerst geringe Kollisionswahrscheinlichkeit haben).
Bytedev

Antworten:

84

Die Verwendung von Guid wäre ein ziemlich guter Weg, aber um etwas zu erhalten, das Ihrem Beispiel ähnelt, möchten Sie es wahrscheinlich in eine Base64-Zeichenfolge konvertieren:

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

Ich werde "=" und "+" los, um Ihrem Beispiel ein wenig näher zu kommen, andernfalls erhalten Sie "==" am Ende Ihrer Zeichenfolge und ein "+" in der Mitte. Hier ist ein Beispiel für eine Ausgabezeichenfolge:

"OZVV5TpP4U6wJthaCORZEQ"

Mark Synowiec
quelle
15
Sie sollten in Betracht ziehen, / zu ersetzen.
Jason Kealey
20
Eine Guid sollte nicht als sichere Zufallszeichenfolge betrachtet werden, da die Reihenfolge erraten werden kann. Ein Guid soll wichtige Konflikte vermeiden, anstatt zufällig zu sein. Es gibt einige gute Diskussionen über die Zufälligkeit eines Guid beim Stapelüberlauf.
Daniel Bradley
Eine klare und kurze Erklärung, worum es Convert.ToBase64Stringgeht, finden Sie hier .
Jwaliszko
2
Kann Guid in base64 konvertiert und + und = ersetzt werden, was die Kollisionswahrscheinlichkeit erhöht?
Milan Aggarwal
5
@SimonEjsing Ich lade Sie zu einem Bier ein, wenn Sie tatsächlich eine Anwendung schreiben können, die bei Verwendung new Guid()ohne "Hacking" (Manipulation der Uhr oder interner Windows-Datenstrukturen) Kollisionen verursacht . Sie können beliebig viele Kerne, Threads, Synchronisationsprimitive usw. verwenden.
Lucero
175

Update 23.01.2016

Wenn Sie diese Antwort nützlich finden, interessieren Sie sich möglicherweise für eine einfache (~ 500 SLOC) Kennwortgenerierungsbibliothek, die ich veröffentlicht habe :

Install-Package MlkPwgen

Dann können Sie zufällige Zeichenfolgen wie in der folgenden Antwort generieren:

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

Ein Vorteil der Bibliothek besteht darin, dass der Code besser herausgerechnet wird, sodass Sie sichere Zufälligkeit für mehr als das Generieren von Zeichenfolgen verwenden können . Schauen Sie sich die Projektseite anWeitere Informationen finden .

Ursprüngliche Antwort

Da noch niemand sicheren Code bereitgestellt hat, poste ich Folgendes, falls jemand es nützlich findet.

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

Vielen Dank an Ahmad für den Hinweis, wie der Code auf .NET Core funktioniert.

Michael Kropat
quelle
Die @ Keltex-Lösung funktionierte für mich nicht richtig (sie gab nach wenigen Anwendungen dieselbe Zeichenfolge zurück). Diese Lösung funktioniert perfekt :)
JoanComasFdz
2
@ LeeGrissom, Vorspannung ist ein wichtiger Aspekt. Nehmen wir zum Beispiel an, Ihr Alphabet enthält 255 Zeichen und Sie erhalten einen zufälligen Wert zwischen 0 und 255. In einem Ringpuffer würden sowohl der Wert 0 als auch der Wert 255 demselben Zeichen entsprechen, was das Ergebnis zugunsten des ersten Zeichens im Alphabet verzerren würde, es wäre weniger zufällig. ob dies wichtig ist, hängt natürlich von der anwendung ab.
Oskar Sjöberg
4
Wer zielt .netcore: Ersetzen var rng = new RNGCryptoServiceProvider()durchvar rng = RandomNumberGenerator.Create()
amd
1
Warum berechnen Sie 'var outOfRangeStart = byteSize - (byteSize% allowCharSet.Length);' für jede Iteration? Sie können es vor der Verwendung berechnen.
Mtkachenko
1
@ BartCalixto behoben. Vielen Dank!
Michael Kropat
38

Ich würde darauf hinweisen, dass GUIDs keine Zufallszahlen sind . Sie sollten nicht als Grundlage verwendet werden, um etwas zu generieren, von dem Sie erwarten, dass es völlig zufällig ist (siehe http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

Die Kryptoanalyse des WinAPI-GUID-Generators zeigt, dass man, da die Folge von V4-GUIDs pseudozufällig ist, angesichts des Anfangszustands bis zu 250.000 GUIDs vorhersagen kann, die von der Funktion UuidCreate zurückgegeben werden. Aus diesem Grund sollten GUIDs in der Kryptografie nicht verwendet werden, z. B. als Zufallsschlüssel.

Verwenden Sie stattdessen einfach die C # Random-Methode. So etwas ( Code hier gefunden ):

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

GUIDs sind in Ordnung, wenn Sie etwas Einzigartiges möchten (wie einen eindeutigen Dateinamen oder Schlüssel in einer Datenbank), aber sie sind nicht gut für etwas, das Sie zufällig sein möchten (wie ein Kennwort oder ein Verschlüsselungsschlüssel). Das hängt also von Ihrer Anwendung ab.

Bearbeiten . Microsoft sagt, dass Random auch nicht so toll ist ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Verwenden Sie zum Generieren einer kryptografisch sicheren Zufallszahl, die zum Erstellen eines zufälligen Kennworts geeignet ist, beispielsweise eine von System.Security.Cryptography.RandomNumberGenerator abgeleitete Klasse, z. B. System.Security.Cryptography.RNGCryptoServiceProvider.

Keltex
quelle
5
Die C # -Zufallsklasse ist ebenfalls nicht "zufällig" und für keinen Kryptocode ungeeignet, da es sich um einen klassischen Zufallsgenerator handelt, der von einer bestimmten Startnummer ausgeht. Der gleiche Startwert gibt auch die gleiche Folge von zurückgegebenen Zahlen zurück. Der GUID-Ansatz ist hier schon viel besser dran (nicht "zufällig", sondern "einzigartig").
Lucero
3
@Lucero: Du bist richtig. Microsoft empfiehlt: "Um eine kryptografisch sichere Zufallszahl zu generieren, die zum Erstellen eines zufälligen Kennworts geeignet ist, verwenden Sie beispielsweise eine von System.Security.Cryptography.RandomNumberGenerator abgeleitete Klasse wie System.Security.Cryptography.RNGCryptoServiceProvider."
Keltex
Nun, die Frage besagte bereits, dass er (pseudo-) zufällige eindeutige Zeichenfolgen haben möchte, also keine Kryptoanforderungen oder sogar die Notwendigkeit, einer bestimmten zufälligen Verteilung zu folgen. GUID ist also wahrscheinlich der einfachste Ansatz.
Joey
1
Die Aussage, dass "angesichts des Anfangszustands bis zu 250 000 GUIDs vorhergesagt werden können", scheint eine inhärent wahre Aussage für jedes PRNG zu sein ... Ich bin sicher, dass es auch nicht sicher ist, aber ich bin nicht sicher, ob es viel Wert beim Generieren gibt wirklich zufällige URLs, wenn es das ist, was das OP anstrebt. ;)
Ojrac
1
(+1 sowieso - PRNG Bildung ist wichtig.)
Ojrac
13

Ich habe die @ Michael Kropats-Lösung vereinfacht und eine LINQ-ähnliche Version erstellt.

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
quelle
11

Ich denke nicht, dass sie wirklich zufällig sind, aber ich vermute, das sind einige Hashes.

Wenn ich eine zufällige Kennung benötige, verwende ich normalerweise eine GUID und konvertiere sie in ihre "nackte" Darstellung:

Guid.NewGuid().ToString("n");
Lucero
quelle
Wie @Keltex hervorhob: Die Kryptoanalyse des WinAPI-GUID-Generators zeigt, dass man, da die Folge von V4-GUIDs pseudozufällig ist, angesichts des Anfangszustands bis zu 250.000 GUIDs vorhersagen kann, die von der Funktion UuidCreate zurückgegeben werden.
JoanComasFdz
4

Versuchen Sie eine Kombination zwischen Guid und Time.Ticks

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

Ich bin überrascht, warum es keine CrytpoGraphic-Lösung gibt. Die GUID ist eindeutig, aber nicht kryptografisch sicher . Siehe diese Dotnet-Geige.

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

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

Falls Sie mit einem Guid voranstellen möchten:

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

Eine sauberere alphanumerische Zeichenfolge:

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

Michael Kropats Lösung in 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
quelle
1

Das funktioniert perfekt für mich

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

Dies wurde für verschiedene Sprachen gefragt. Hier ist eine Frage zu Passwörtern die auch hier gelten sollte.

Wenn Sie die Zeichenfolgen für die URL-Verkürzung verwenden möchten, benötigen Sie außerdem ein Wörterbuch <> oder eine Datenbankprüfung, um festzustellen, ob bereits eine generierte ID verwendet wurde.

Pontus Gagge
quelle
0

Wenn Sie eine alphanumerische Zeichenfolge mit Kleinbuchstaben und möchten Großbuchstaben ([a-zA-Z0-9]) möchten, können Sie Convert.ToBase64String () für eine schnelle und einfache Lösung verwenden.

Überprüfen Sie hinsichtlich der Eindeutigkeit das Geburtstagsproblem, um zu berechnen, wie wahrscheinlich eine Kollision ist (A) die Länge der generierten Zeichenfolgen und (B) die Anzahl der generierten Zeichenfolgen.

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
quelle
-1
  • Ich bin nicht sicher, ob der Link von Microsoft zufällig generiert wird
  • Schauen Sie sich das neue Guid () an. ToString ()
Fabian Vilers
quelle
4
Du meinst Guid.NewGuid (). ToString () - Guid hat keinen öffentlichen Konstruktor
cjk
3
Sie haben wahrscheinlich Recht, haben ohne Überprüfung getippt. Ich bin sicher, das Originalplakat hat den Punkt.
Fabian Vilers
-1

Holen Sie sich den eindeutigen Schlüssel mithilfe des GUID-Hash-Codes

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
quelle
Dies funktioniert perfekt, aber zufällige Wörter enthalten keine eindeutigen Zeichen. Die Zeichen wiederholen sich wie 114e3 (zwei Einsen), eaaea (drei a und zwei e), 60207 (zwei Nullen) und so weiter. Wie generiere ich eine zufällige Zeichenfolge ohne Wiederholung von Zeichen mit alphanumerischer Kombination?
Vijay
@vijay: Da es hexadezimale Ziffern ausgibt, beschränken Sie sich auf 16 Zeichen und 16! mögliche Ausgänge. Zufällige Zeichenfolgen sind genau das, zufällig. Sie könnten theoretisch eine Zeichenfolge aller a erhalten (aaaaaaaaaaaaaaa). Es ist sehr unwahrscheinlich, aber nicht mehr als jede andere zufällige Zeichenfolge. Ich bin nicht sicher, warum Sie diese Einschränkung benötigen würden, aber wenn Sie der Zeichenfolge Zeichen hinzufügen, fügen Sie sie in ein HashSet <T> ein, überprüfen Sie ihre Existenz und fügen Sie sie der Zeichenfolge hinzu oder überspringen Sie sie entsprechend.
Chris Doggett