108

I'd like to generate random unique strings like the ones being generated by MSDN library.(Error Object), for example. A string like 't9zk6eay' should be generated.

Edgar
  • 6,022
  • 8
  • 33
  • 66
Kirtan
  • 21,295
  • 6
  • 46
  • 61
  • 1
    try this `string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);` more can be found [here](http://stackoverflow.com/a/1344365/2218697) – Shaiju T Jun 24 '15 at 10:35
  • 1
    For something to be totally unique it must be based on something non random, like time, location etc. and therefore can never actually be fully random. A Guid may seem random but in reality it is not. IMO your only hope is to make it so random and complex that for all practical purposes the values will be unique (i.e. have a extremely low probability of collision). – bytedev Dec 17 '18 at 10:48

13 Answers13

186

Update 2016/1/23

If you find this answer useful, you may be interested in a simple (~500 SLOC) password generation library I published:

Install-Package MlkPwgen

Then you can generate random strings just like in the answer below:

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

One advantage of the library is that the code is better factored out so you can use secure randomness for more than generating strings. Check out the project site for more details.

Original Answer

Since no one has provided secure code yet, I post the following in case anyone finds it useful.

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

Thanks to Ahmad for pointing out how to get the code working on .NET Core.

Michael Kropat
  • 14,557
  • 12
  • 70
  • 91
  • @Keltex solution wasn't working right for meh (it was returning the same string after few uses). This solution works perfect :) – JoanComasFdz Jun 07 '12 at 13:47
  • I think if() condition should be adjusted: theoretically it is possible to reach limit of i=buf.Length and still result.Length could be less than length because of all values in buffer>=outOfRangeStart. so I suggest: if (outOfRangeStart <= buf[i] && i < buf.Length - 1) { continue; } – 4pie0 Jan 14 '13 at 15:57
  • also result.ToString(): explicit conversion is not required here – 4pie0 Jan 14 '13 at 16:00
  • @cf16 In the case of i=buf.Length && result.Length < length, the while loop condition holds true, so buf gets refilled with more random values and it all repeats until result.Length = length -- hence not a bug. That's what you're talking about, right? Also, thanks for the tip about the unnecessarily explicit conversion -- I plan to update the code when I'm no longer on the road. – Michael Kropat Jan 15 '13 at 10:26
  • not exactly: while condition still will be true, but it is theoretically possible that all buf values are greater than outOfRangeStart again and again, so you have to put something finally to the result if return string cannot be empty. (outOfRangeStart <= buf[i] && i < buf.Length - 1) { continue; } does the thing. now, if the last buf is too large too, but we are at the end of buf - we put some character into result – 4pie0 Jan 16 '13 at 01:56
  • +1 It's a clever routine, but I'm not sure why you need to worry about "biasing". Your allowedChars set is just a ring-buffer, and you could simplify your code by treating it that way. Am I overlooking something? – Lee Grissom Mar 29 '13 at 21:29
  • 3
    @LeeGrissom, biasing is an important aspect. Lets say for example that your alphabet contains 255 characters and you get a random value between 0-255. In a ring buffer both the value 0 and 255 would correspond to the same character which would skew the result in favor of the first character in the alphabet, it would be less random. if this matters depends on the application of course. – Oskar Sjöberg Mar 12 '15 at 16:24
  • @MichaelKropat : will it work for storing it in database, i want to use it to generate unique strings for a table?,thanks in advance! – Srinivas Rathikrindi Feb 05 '16 at 06:57
  • So I'm wondering, if I were to use this routine as a token generator, would I still need to salt/hash it? – rumblefx0 Mar 29 '16 at 15:18
  • 4
    Who's targeting `.netcore`: Replace `var rng = new RNGCryptoServiceProvider()` with `var rng = RandomNumberGenerator.Create()` – amd Jan 26 '17 at 13:39
  • Might be worth elaborating on the "unique" aspect of this solution. What are the chances of a collision? – Jon Feb 12 '17 at 22:53
  • 2
    Why do you calculate 'var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);' for each iteration? You can calculate it before 'using'. – mtkachenko Apr 12 '17 at 11:37
  • `allowedCharSet` array is not necessary. `allowedChars` (a string) is already an array of chars. – kspearrin Jun 29 '17 at 19:44
  • @kspearrin: the only purpose of using a set is to prevent a user from passing in a string that contains multiples of the same character, which could bias the output unintentionally. If you trust callers to not make that mistake, you can safely omit converting to a set. – Michael Kropat Jun 29 '17 at 20:04
  • `new System.Security.Cryptography.RandomNumberGenerator.Create()` should be `System.Security.Cryptography.RandomNumberGenerator.Create()` without the `new` keyword. It's an static method. – Bart Calixto Dec 10 '18 at 22:49
  • 1
    @BartCalixto Fixed. Thanks! – Michael Kropat Dec 19 '18 at 17:10
  • can anybody help me I have tried to use this method but getting error ***length is a field but used like type*** – Atul Mathew Jul 03 '19 at 12:17
  • Nice. Is there a way to force password to contain: upper, lower alphabet, digits, non-alphanumeric. – Mu nt Jun 10 '23 at 16:08
97

Using Guid would be a pretty good way, but to get something looking like your example, you probably want to convert it to a Base64 string:

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

I get rid of "=" and "+" to get a little closer to your example, otherwise you get "==" at the end of your string and a "+" in the middle. Here's an example output string:

"OZVV5TpP4U6wJthaCORZEQ"

Mark Synowiec
  • 5,385
  • 1
  • 22
  • 18
  • 16
    You should consider replacing / too. – Jason Kealey Jun 04 '09 at 21:06
  • 22
    A Guid should not be considered as a secure random string as the sequence can be guessed. A Guid is designed for avoiding key conflicts, rather than being random. There are some good [discussions of the randomness of a Guid](http://stackoverflow.com/a/290463/366550) around on stack overflow. – Daniel Bradley Sep 24 '13 at 10:46
  • For clear and short explanation of what `Convert.ToBase64String` is about, [take a look here](http://davidzych.com/2013/12/23/writing-your-own-convert-tobase64string-in-c/). – jwaliszko Sep 21 '14 at 14:34
  • 2
    Can converting guid into base64 and replacing + and = increasing collision probability? – Milan Aggarwal Jan 07 '15 at 07:14
  • Be very careful about GUIDs their uniqueness depends on the machine clock, it is easy to get collisions in multi-threaded application if two threads happens to generate the GUID within a very small time span. – Simon Ejsing Aug 15 '16 at 19:03
  • @SimonEjsing even if a multi threaded application were to async generate a guid the cpu can only process one command at a time so it would be impossible to generate a duplicate guid no? what do you think? – TheAkhemist Aug 26 '16 at 15:06
  • First of, most modern computers have multiple logical cores if not multiple physical cores, so you can't assume instructions are serialized. Even considering a single core machine I doubt that it matters, the GUID generation is not tied to which instruction the CPU executed and you execute hundreds of instructions per millisecond. I brought it up because I recently hit this issue where an application I was developing generated duplicate GUIDs under load testing. – Simon Ejsing Sep 02 '16 at 16:44
  • 8
    @SimonEjsing I'll invite you to a beer if you can actually write an application that gets collisions when using `new Guid()` without "hacking" (tampering with clock or internal Windows data structures). Feel free to use as many cores, threads, synchronization primitives etc. as you like. – Lucero Nov 03 '16 at 11:54
40

I would caution that GUIDs are not random numbers. They should not be used as the basis to generate anything that you expect to be totally random (see http://en.wikipedia.org/wiki/Globally_Unique_Identifier):

Cryptanalysis of the WinAPI GUID generator shows that, since the sequence of V4 GUIDs is pseudo-random, given the initial state one can predict up to next 250 000 GUIDs returned by the function UuidCreate. This is why GUIDs should not be used in cryptography, e. g., as random keys.

Instead, just use the C# Random method. Something like this (code found here):

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 are fine if you want something unique (like a unique filename or key in a database), but they are not good for something you want to be random (like a password or encryption key). So it depends on your application.

Edit. Microsoft says that Random is not that great either (http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):

To generate a cryptographically secure random number suitable for creating a random password, for example, use a class derived from System.Security.Cryptography.RandomNumberGenerator such as System.Security.Cryptography.RNGCryptoServiceProvider.

Community
  • 1
  • 1
Keltex
  • 26,220
  • 11
  • 79
  • 111
  • 5
    The C# random class is not "random" either and unsuitable for any crypto code, since it is a classic random generator starting from a specific seed number. Same seed will also return the same sequence of numbers returned; the GUID approach is already much better off here (not "random" but "unique"). – Lucero Apr 08 '09 at 14:51
  • 3
    @Lucero: You're correct. Microsoft recommends, "To generate a cryptographically secure random number suitable for creating a random password, for example, use a class derived from System.Security.Cryptography.RandomNumberGenerator such as System.Security.Cryptography.RNGCryptoServiceProvider." – Keltex Apr 08 '09 at 14:53
  • Well, the question already stated that he wants (pseudo-)random unique strings, so no crypto requirements or even a need for following a specific random distribution. So GUID is probably the easiest approach. – Joey Apr 08 '09 at 14:58
  • 1
    The statement that "given the initial state one can predict up to next 250 000 GUIDs" seems like an inherently true statement for *any* PRNG... I'm sure it's also not secure, but I'm not sure there's much value in generating truly random URLs, if that's what the OP is going for. ;) – ojrac Apr 08 '09 at 14:58
  • 1
    (+1 anyway -- PRNG education is important.) – ojrac Apr 08 '09 at 15:00
14

I don't think that they really are random, but my guess is those are some hashes.

Whenever I need some random identifier, I usually use a GUID and convert it to its "naked" representation:

Guid.NewGuid().ToString("n");
Lucero
  • 59,176
  • 9
  • 122
  • 152
  • As @Keltex pointed out: Cryptanalysis of the WinAPI GUID generator shows that, since the sequence of V4 GUIDs is pseudo-random, given the initial state one can predict up to next 250 000 GUIDs returned by the function UuidCreate. – JoanComasFdz Jun 07 '12 at 13:49
13

I simplified @Michael Kropats solution and made a LINQ-esque version.

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
  • 2,728
  • 27
  • 31
4

Try combination between Guid and Time.Ticks

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

I am surprised why there is not a CrytpoGraphic solution in place. GUID is unique but not cryptographically safe. See this 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);

In case you want to Prepend with a Guid:

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

A cleaner alphanumeric string:

result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);
tika
  • 7,135
  • 3
  • 51
  • 82
2

This works perfect for me

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

Michael Kropats solution 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
  • 959
  • 6
  • 6
0

If you want an alphanumeric strings with lowercase and uppercase characters ([a-zA-Z0-9]), you can use Convert.ToBase64String() for a fast and simple solution.

As for uniqueness, check out the birthday problem to calculate how likely a collission is given (A) the length of the strings generated and (B) the number of strings generated.

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
  • 7,992
  • 4
  • 49
  • 67
0

This has been asked for various languages. Here's one question about passwords which should be applicable here as well.

If you want to use the strings for URL shortening, you'll also need a Dictionary<> or database check to see whether a generated ID has already been used.

Community
  • 1
  • 1
Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
-1
  • not sure Microsoft's link are randomly generated
  • have a look to new Guid().ToString()
Fabian Vilers
  • 2,892
  • 2
  • 24
  • 30
-1

Get Unique Key using GUID Hash code

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
  • 19,959
  • 4
  • 61
  • 86
  • This works perfectly but random words are not containing unique characters. The characters are repeating, like 114e3(two 1's),eaaea(three a's and two e's), 60207(two 0's) and so on. How to generate a Random string with no repetition of characters with alphanumeric combination? – vijay Jan 09 '12 at 18:50
  • @vijay: Since it's outputting hex digits, you're limiting yourself to 16 characters, and 16! possible outputs. Random strings are just that, random. You could theoretically get a string of all a's (aaaaaaaaaaaaaaa). It's very improbable, but no more so than any other random string. I'm not sure why you'd need that constraint, but as you're adding characters to the string, pop them in a HashSet, check for their existence, and add them to the string or skip them accordingly. – Chris Doggett Jan 09 '12 at 20:08