Unique random string generation

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.


Solution 1:

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:

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:

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:

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:

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