web-dev-qa-db-fra.com

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

J'aimerais générer des chaînes uniques aléatoires comme celles générées par la bibliothèque MSDN:

http://msdn.Microsoft.com/en-us/library/t9zk6eay.aspx , par exemple. Une chaîne telle que 't9zk6eay' devrait être générée.

86
Kirtan

Utiliser Guid serait un très bon moyen, mais pour obtenir quelque chose qui ressemble à votre exemple, vous souhaiterez probablement le convertir en 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 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"

68
Mark Synowiec

Mise à jour 2016/1/23

Si vous trouvez cette réponse utile, vous pouvez être intéressé par une bibliothèque de génération de mot 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 un caractère aléatoire sécurisé pour plus que générer des chaînes . Découvrez le site du projet pour plus de détails.

Réponse originale

Comme personne n’a encore fourni de code sécurisé, j’envoie ce qui suit au cas où quelqu'un le jugerait 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 d’avoir expliqué comment faire fonctionner le code sur .NET Core.

165
Michael Kropat

Je mets en garde que les GUID 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 WinAPI GUID montre que, puisque la séquence des GUID V4 est pseudo-aléatoire, compte tenu de l'état initial, on peut prédire jusqu'à 250 000 GUID suivants renvoyés par la fonction UuidCreate. C'est pourquoi les GUID ne doivent pas être utilisés en cryptographie, e. g., en tant que clés aléatoires.

Au lieu de cela, utilisez simplement la méthode aléatoire C #. Quelque chose comme ceci ( 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 unique (comme un nom de fichier unique ou une clé dans une base de données), mais ils ne conviennent pas à quelque chose que vous voulez être random (comme un mot de passe ou une clé de cryptage). Cela dépend donc de votre application.

Modifier. Microsoft affirme que Random n’est pas très bon non plus ( http://msdn.Microsoft.com/en-us/library/system.random(VS.71).aspx ):

Par exemple, pour générer un nombre aléatoire sécurisé sur le plan cryptographique et approprié pour créer un mot de passe aléatoire, utilisez une classe dérivée de System.Security.Cryptography.RandomNumberGenerator telle que System.Security.Cryptography.RNGCryptoServiceProvider.

37
Keltex

Je ne pense pas qu’ils soient vraiment aléatoires, mais j’imagine qu’il s’agit de hachages.

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

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

J'ai simplifié la solution @Michael Kropats et réalisé 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();
    }   
}
11
Oskar Sjöberg

Essayez une combinaison entre Guid et Time.Ticks

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

La 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
1
jhersey29

Je suis surpris de la raison pour laquelle aucune solution CrytpoGraphic n’est en place. GUID est unique mais non cryptographiquement sûr}. Voir ce violon Dotnet.

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

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

Si 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);
1
tika

Si vous voulez une chaîne alphanumérique avec des lettres 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 birthday pour calculer la probabilité d’une collission (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)
0
Timo

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

Cela a été demandé pour différentes langues. Voici une question sur les mots de passe qui devrait être applicable ici aussi. 

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

0
Pontus Gagge