Génération de chaîne aléatoire unique

98

Je voudrais générer des chaînes uniques aléatoires comme celles générées par la bibliothèque MSDN ( objet d'erreur ), par exemple. Une chaîne comme «t9zk6eay» doit être générée.

Kirtan
la source
1
essayez ceci string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);plus peut être trouvé ici
shaijut
1
Pour que quelque chose soit totalement unique, il doit être basé sur quelque chose de non aléatoire, comme l'heure, le lieu, etc. et ne peut donc jamais être totalement aléatoire. Un Guid peut sembler aléatoire mais en réalité ce n'est pas le cas. IMO, votre seul espoir est de le rendre si aléatoire et complexe qu'à toutes fins pratiques, les valeurs seront uniques (c'est-à-dire qu'elles auront une probabilité de collision extrêmement faible).
bytedev

Réponses:

85

Utiliser Guid serait un très bon moyen, mais pour obtenir quelque chose qui ressemble à votre exemple, vous voudrez probablement le convertir en une chaîne Base64:

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

Je me débarrasse de "=" et "+" pour me rapprocher un peu plus de votre exemple, sinon vous obtenez "==" à la fin de votre chaîne et un "+" au milieu. Voici un exemple de chaîne de sortie:

«OZVV5TpP4U6wJthaCORZEQ»

Mark Synowiec
la source
15
Vous devriez envisager de remplacer / aussi.
Jason Kealey
20
Un Guid ne doit pas être considéré comme une chaîne aléatoire sécurisée car la séquence peut être devinée. Un Guid est conçu pour éviter les conflits clés, plutôt que d'être aléatoire. Il y a de bonnes discussions sur le caractère aléatoire d'un Guid autour du débordement de pile.
Daniel Bradley
Pour une explication claire et brève de ce dont il Convert.ToBase64Strings'agit, jetez un œil ici .
jwaliszko
2
Peut-on convertir guid en base64 et remplacer + et = augmenter la probabilité de collision?
Milan Aggarwal
6
@SimonEjsing Je vous invite à boire une bière si vous pouvez réellement écrire une application qui obtient des collisions lors de l'utilisation new Guid()sans "piratage" (falsification de l'horloge ou des structures de données Windows internes). N'hésitez pas à utiliser autant de cœurs, de threads, de primitives de synchronisation, etc. que vous le souhaitez.
Lucero
175

Mise à jour 2016/1/23

Si vous trouvez cette réponse utile, vous pourriez être intéressé par une bibliothèque de génération de mots de passe simple (~ 500 SLOC) que j'ai publiée :

Install-Package MlkPwgen

Ensuite, vous pouvez générer des chaînes aléatoires comme dans la réponse ci-dessous:

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

L'un des avantages de la bibliothèque est que le code est mieux factorisé afin que vous puissiez utiliser le caractère aléatoire sécurisé pour plus que générer des chaînes . Consultez le site du projet pour plus de détails.

Réponse originale

Comme personne n'a encore fourni de code sécurisé, je poste ce qui suit au cas où quelqu'un le trouverait utile.

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

Merci à Ahmad pour avoir indiqué comment faire fonctionner le code sur .NET Core.

Michael Kropat
la source
La solution @Keltex ne fonctionnait pas bien pour moi (elle retournait la même chaîne après quelques utilisations). Cette solution fonctionne parfaitement :)
JoanComasFdz
2
@LeeGrissom, le biais est un aspect important. Disons par exemple que votre alphabet contient 255 caractères et que vous obtenez une valeur aléatoire entre 0 et 255. Dans un tampon en anneau, les valeurs 0 et 255 correspondraient au même caractère qui fausserait le résultat en faveur du premier caractère de l'alphabet, ce serait moins aléatoire. si cela compte dépend de l'application bien sûr.
Oskar Sjöberg
4
Qui est le ciblage .netcore: Remplacer var rng = new RNGCryptoServiceProvider()parvar rng = RandomNumberGenerator.Create()
amd
1
Pourquoi calculez-vous 'var outOfRangeStart = byteSize - (byteSize% allowedCharSet.Length);' pour chaque itération? Vous pouvez le calculer avant «d'utiliser».
mtkachenko
1
@BartCalixto Fixe. Merci!
Michael Kropat
38

Je vous avertis que les GUID ne sont pas des nombres aléatoires . Ils ne doivent pas être utilisés comme base pour générer tout ce que vous pensez être totalement aléatoire (voir http://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

L'analyse cryptographique du générateur de GUID WinAPI montre que, puisque la séquence des GUID V4 est pseudo-aléatoire, étant donné l'état initial, on peut prévoir jusqu'à 250 000 GUID suivants renvoyés par la fonction UuidCreate. C'est pourquoi les GUID ne doivent pas être utilisés en cryptographie, par exemple en tant que clés aléatoires.

Au lieu de cela, utilisez simplement la méthode C # Random. Quelque chose comme ça ( code trouvé ici ):

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

Les GUID conviennent si vous voulez quelque chose d' unique (comme un nom de fichier ou une clé unique dans une base de données), mais ils ne sont pas bons pour quelque chose que vous voulez être aléatoire (comme un mot de passe ou une clé de cryptage). Cela dépend donc de votre application.

Modifier . Microsoft dit que Random n'est pas terrible non plus ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

Pour générer un nombre aléatoire sécurisé par cryptographie adapté à la création d'un mot de passe aléatoire, par exemple, utilisez une classe dérivée de System.Security.Cryptography.RandomNumberGenerator telle que System.Security.Cryptography.RNGCryptoServiceProvider.

Keltex
la source
5
La classe aléatoire C # n'est pas non plus "aléatoire" et ne convient à aucun code cryptographique, car il s'agit d'un générateur aléatoire classique à partir d'un numéro de départ spécifique. La même valeur de départ renverra également la même séquence de nombres renvoyée; l'approche GUID est déjà bien meilleure ici (pas "aléatoire" mais "unique").
Lucero
3
@Lucero: Vous avez raison. Microsoft recommande, «Pour générer un nombre aléatoire sécurisé par cryptographie adapté à la création d'un mot de passe aléatoire, par exemple, utilisez une classe dérivée de System.Security.Cryptography.RandomNumberGenerator telle que System.Security.Cryptography.RNGCryptoServiceProvider».
Keltex
Eh bien, la question indiquait déjà qu'il voulait des chaînes uniques (pseudo-) aléatoires, donc aucune exigence de crypto ni même un besoin de suivre une distribution aléatoire spécifique. Ainsi, GUID est probablement l'approche la plus simple.
Joey
1
La déclaration selon laquelle "étant donné l'état initial, on peut prédire jusqu'à 250 000 GUID suivants" semble être une déclaration intrinsèquement vraie pour tout PRNG ... Je suis sûr que ce n'est pas non plus sécurisé, mais je ne suis pas sûr qu'il y ait beaucoup de valeur à générer URL vraiment aléatoires, si c'est ce que l'OP vise. ;)
ojrac
1
(+1 de toute façon - l'éducation PRNG est importante.)
ojrac
13

J'ai simplifié la solution @Michael Kropats et créé une version 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
la source
11

Je ne pense pas qu'ils soient vraiment aléatoires, mais je suppose que ce sont des hachages.

Chaque fois que j'ai besoin d'un identifiant aléatoire, j'utilise généralement un GUID et je le convertis en sa représentation "nue":

Guid.NewGuid().ToString("n");
Lucero
la source
Comme @Keltex l'a souligné: L'analyse cryptographique du générateur de GUID WinAPI montre que, puisque la séquence des GUID V4 est pseudo-aléatoire, étant donné l'état initial, on peut prédire jusqu'à 250 000 GUID suivants renvoyés par la fonction UuidCreate.
JoanComasFdz
4

Essayez la combinaison entre Guid et Time.

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

Je suis surpris qu'il n'y ait pas de solution CrytpoGraphic en place. Le GUID est unique mais non sécurisé sur le plan cryptographique . Voir ce Dotnet Fiddle.

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

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

Dans le cas où vous souhaitez ajouter un Guid:

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

Une chaîne alphanumérique plus propre:

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

Solution Michael Kropats dans 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
la source
1

Cela fonctionne parfaitement pour moi

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

Cela a été demandé pour différentes langues. Voici une question sur les mots de passe qui devrait également s'appliquer ici.

Si vous souhaitez utiliser les chaînes pour raccourcir l'URL, vous aurez également besoin d'une vérification du dictionnaire <> ou de la base de données pour voir si un ID généré a déjà été utilisé.

Pontus Gagge
la source
0

Si vous voulez une chaîne alphanumérique avec des caractères minuscules et majuscules ([a-zA-Z0-9]), vous pouvez utiliser Convert.ToBase64String () pour une solution simple et rapide.

En ce qui concerne l'unicité, consultez le problème de l' anniversaire pour calculer la probabilité qu'une collission soit donnée (A) la longueur des chaînes générées et (B) le nombre de chaînes générées.

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
la source
-1
  • pas sûr que le lien de Microsoft soit généré de manière aléatoire
  • jetez un œil au nouveau Guid (). ToString ()
Fabian Vilers
la source
4
Vous voulez dire Guid.NewGuid (). ToString () - Guid n'a pas de constructeur public
cjk
3
Vous avez probablement raison, tapiez sans vérification. Je suis sûr que l'affiche originale a le point.
Fabian Vilers
-1

Obtenir une clé unique à l'aide du code de hachage 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
la source
Cela fonctionne parfaitement mais les mots aléatoires ne contiennent pas de caractères uniques. Les caractères se répètent, comme 114e3 (deux 1), eaaea (trois a et deux e), 60207 (deux 0) et ainsi de suite. Comment générer une chaîne aléatoire sans répétition de caractères avec combinaison alphanumérique?
vijay
@vijay: Comme il affiche des chiffres hexadécimaux, vous vous limitez à 16 caractères et 16! sorties possibles. Les chaînes aléatoires ne sont que cela, aléatoires. Vous pourriez théoriquement obtenir une chaîne de tous les a (aaaaaaaaaaaaaaa). C'est très improbable, mais pas plus que n'importe quelle autre chaîne aléatoire. Je ne sais pas pourquoi vous auriez besoin de cette contrainte, mais lorsque vous ajoutez des caractères à la chaîne, insérez-les dans un HashSet <T>, vérifiez leur existence et ajoutez-les à la chaîne ou ignorez-les en conséquence.
Chris Doggett