Unique random string generation

C#Random

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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"

Solution 3 - C#

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.

Solution 4 - C#

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

Solution 5 - C#

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

Solution 6 - C#

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]+", "");

Solution 7 - C#

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

Solution 8 - C#

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

Solution 9 - C#

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

Solution 10 - C#

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.

Solution 11 - C#

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)

Solution 12 - C#

  • not sure Microsoft's link are randomly generated
  • have a look to new Guid().ToString()

Solution 13 - C#

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKirtanView Question on Stackoverflow
Solution 1 - C#Michael KropatView Answer on Stackoverflow
Solution 2 - C#Mark SynowiecView Answer on Stackoverflow
Solution 3 - C#KeltexView Answer on Stackoverflow
Solution 4 - C#LuceroView Answer on Stackoverflow
Solution 5 - C#Oskar SjöbergView Answer on Stackoverflow
Solution 6 - C#ASalamehView Answer on Stackoverflow
Solution 7 - C#tikaView Answer on Stackoverflow
Solution 8 - C#MarlinGView Answer on Stackoverflow
Solution 9 - C#jhersey29View Answer on Stackoverflow
Solution 10 - C#Pontus GaggeView Answer on Stackoverflow
Solution 11 - C#TimoView Answer on Stackoverflow
Solution 12 - C#Fabian VilersView Answer on Stackoverflow
Solution 13 - C#Chris DoggettView Answer on Stackoverflow