1098

How can I generate a random 8 character alphanumeric string in C#?

Andrew
  • 13,934
  • 8
  • 78
  • 93
KingNestor
  • 59,315
  • 50
  • 115
  • 149
  • 2
    What restrictions if any do you have on the character set? Just English language characters and 0-9? Mixed case? – Eric J. Aug 27 '09 at 23:15
  • 8
    Or maybe to http://stackoverflow.com/questions/730268/unique-random-string-generation or http://stackoverflow.com/questions/1122483/c-random-string-generator – Jonas Elfström Aug 27 '09 at 23:15
  • 9
    Note that you should NOT use any method based on the `Random` class to generate passwords. The seeding of `Random` has very low entropy, so it's not really secure. Use a cryptographic PRNG for passwords. – CodesInChaos Mar 09 '11 at 18:47
  • 2
    Would be nice to include language localisation in this question. Especially if your gui needs to cater for Chinese or Bulgarian! – Peter Jamsmenson Sep 25 '15 at 23:48
  • Also see [How to make random string of numbers and letters with a length of 5?](http://stackoverflow.com/q/9995839/608639) – jww Feb 25 '17 at 07:39
  • 18
    Something with this many upvotes and this many quality answers doesn't deserved to be marked as closed. I vote that it be reopened. – John Coleman Oct 16 '17 at 12:52

33 Answers33

1859

I heard LINQ is the new black, so here's my attempt using LINQ:

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)

Liam
  • 22,818
  • 25
  • 93
  • 157
dtb
  • 198,715
  • 31
  • 379
  • 417
  • We would need a performance test on this one btw. Maybe on generating 8000 instead of 8 characters. – Alex Aug 27 '09 at 23:43
  • 28
    @Alex: I've run a few quick tests and it seems to scale pretty much linearly when generating longer strings (so long as there's actually enough memory available). Having said that, Dan Rigby's answer was almost twice as fast as this one in every test. – LukeH Aug 28 '09 at 00:33
  • 6
    Well. If your criteria is that it uses linq and that it has a lousy code narrative then it's definitely the bee's knees. Both the code narrative and actual path of execution is rather inefficient and indirect. Don't get me wrong, I'm a huge code hipster (i love python), but this is pretty much a rube goldberg machine. – eremzeit Sep 29 '11 at 15:41
  • 6
    While this technically answers the question, it's output is very misleading. Generating 8 random characters sounds like there can be *very* many results, whereas this at best produces 2 billion different results. And in practice even fewer. You should also add a BIG FAT warning to not use this for anything security related stuff. – CodesInChaos Mar 17 '12 at 20:50
  • 1
    It isn't truly using LINQ until you drop the ToArray() so the query will be executed lazily. – mao47 Jul 16 '13 at 19:19
  • @mao47: The query needs to be executed anyway, since the result is a string and not an IEnumerable. – dtb Jul 16 '13 at 19:20
  • @dtb - By the way, why did you skip the lowercase letters. – xaisoft Sep 30 '13 at 14:27
  • 49
    @xaisoft: Lowercase letters are left as an exercise for the reader. – dtb Sep 30 '13 at 14:29
  • Although this works, this is wasteful/inefficient since Enumerable.Repeat(chars, length) will create multiple identical strings of the requested length, only to then discard each of these strings and replace with a random character, then create a byte array only to convert back into a string. I like linq as much a the next, but in this case a regular loop and a StringBuilder would be a lot more efficient. – stoj May 20 '16 at 01:06
  • Using the [MedallionRandom](https://github.com/madelson/MedallionUtilities/tree/master/MedallionRandom) package, you can easily adapt this code to use `RNGCryptoServiceProvider` instead of `System.Random` – ChaseMedallion Oct 27 '16 at 11:26
  • 19
    The following line is more memory (and thus time) efficient than the given one `return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());` – Tyson Williams Nov 11 '16 at 18:06
  • @stoj I had the same first instinct about wasting memory creating duplicate strings, but that's not what happens. `Enumerable.Repeat` won't create multiple identical strings; it merely creates multiple references to the same string. – Mark Amery Sep 09 '17 at 14:57
  • 1
    Using linq makes the code pretty, but also introduces all the performance issues related to linq; It's ment to be pretty and "easy", not fast. If you need to call this often (e.g. several times per second) you should find a more efficient snippet that does the same. If you call this like once per 10 minutes, then you don't need to worry about it. – JohnF Mar 19 '20 at 08:01
422
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

Not as elegant as the Linq solution.

(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)

TylerH
  • 19,065
  • 49
  • 65
  • 86
Dan Rigby
  • 15,109
  • 6
  • 38
  • 58
  • 5
    @Alex: This isn't the absolute fastest answer, but it is the fastest "real" answer (ie, of those that allow control over the characters used and the length of the string). – LukeH Aug 28 '09 at 00:56
  • 2
    @Alex: Adam Porad's `GetRandomFileName` solution is quicker but doesn't allow any control of the characters used and the max possible length is 11 chars. Douglas's `Guid` solution is lightning-fast but the characters are restricted to A-F0-9 and the max possible length is 32 chars. – LukeH Aug 28 '09 at 01:01
  • You could get more than 11 chars using the GetRandomFileName solution, by getting multiple strings from the GetRandomeFileName method and concatonating. You're right about limited character set -- only lowercase a-z and numbers. Not sure how that would compare performance-wise if you're trying to generate a long random string. – Adam Porad Aug 28 '09 at 03:46
  • 1
    @Adam: Yes, you could concat the result of multiple calls to `GetRandomFileName` but then (a) you'd lose your performance advantage, and (b) your code would become more complicated. – LukeH Aug 28 '09 at 09:08
  • @DanRigby - Am I doing something wrong, most of my 1,000,000 iterations generate the same password. I am in a for loop and in each iteration I generate a new password and then check if my existing list already contains that password, over 999,000+ are dupes. – xaisoft Sep 30 '13 at 14:21
  • 3
    @xaisoft create your instance of the Random() object outside of your loop. If you create lots of instances of Random() in a short interval then the call to .Next() will return the same value as Random() uses a time based seed. – Dan Rigby Sep 30 '13 at 18:16
  • @DanRigby - Thanks, I did that and it worked. Question: If I create an instance of random and then use it to create a password and then wait 5 minutes or even just 5 seconds, how likely is the new password going to be different from the previous one? – xaisoft Sep 30 '13 at 18:54
  • Time isn't a factor outside of the creation of the Random() object. But to answer your question, there's no guarantee of uniqueness. It would be difficult to quantify the exact probability in most cases as the more possible values there, the more likely the next value will be different. For the code listed above, most of the time the values are different from the previous. – Dan Rigby Sep 30 '13 at 19:12
  • 2
    @xaisoft Don't use this answer for anything security critical, like passwords. `System.Random` is not suitable for security. – CodesInChaos Oct 21 '13 at 10:14
  • Short and sweet, when one does not utilize the greater extent / specific unique function of a class it is usually not worth while for bulk operations. `Enumerable` will never beat a small `for` loop in efficiency – LokizFenrir Jan 07 '15 at 08:41
  • Out of curiosity, why are you using weak-typed variables? – Christine Jul 24 '16 at 19:05
  • 1
    @Hill It looks like you are saying that using var implies weak tying. It does not :) https://msdn.microsoft.com/en-us/library/bb383973.aspx – Francois Beaussier Dec 11 '16 at 06:42
  • 1
    @FrancoisBeaussier - I call semi-BS on that article though (yes, I'm challenging MSDN). From my understanding, weak-typed variables are those that aren't implicitly defined by the programmer... regardless of what the compiler does. But, upon further research, it seems that "strong type" and "weak type" are a bit more subjective than I thought. Even the Wikipedia article for "Strong and Weak Typing" admits that there's no clear definition. https://en.wikipedia.org/wiki/Strong_and_weak_typing – Christine Dec 12 '16 at 17:11
  • `private static readonly Random Random = new Random(); private static string CreateRandomText(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var charArray = new char[length] .Select(c => chars[Random.Next(chars.Length)]) .ToArray(); return new string(charArray); }` – Mladen B. Nov 08 '17 at 12:40
  • `chars` and `stringChars` names seem swapped – bugged87 Jun 09 '18 at 02:22
  • 1
    This is not C#. – Fandango68 Nov 22 '19 at 06:01
368

UPDATED based on comments. The original implementation generated a-h ~1.95% of the time and the remaining characters ~1.56% of the time. The update generates all characters ~1.61% of the time.

FRAMEWORK SUPPORT - .NET Core 3 (and future platforms that support .NET Standard 2.1 or above) provides a cryptographically sound method RandomNumberGenerator.GetInt32() to generate a random integer within a desired range.

Unlike some of the alternatives presented, this one is cryptographically sound.

using System;
using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        internal static readonly char[] chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 

        public static string GetUniqueKey(int size)
        {            
            byte[] data = new byte[4*size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                var rnd = BitConverter.ToUInt32(data, i * 4);
                var idx = rnd % chars.Length;

                result.Append(chars[idx]);
            }

            return result.ToString();
        }

        public static string GetUniqueKeyOriginal_BIASED(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

Based on a discussion of alternatives here and updated/modified based on the comments below.

Here's a small test harness that demonstrates the distribution of characters in the old and updated output. For a deep discussion of the analysis of randomness, check out random.org.

using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}
Community
  • 1
  • 1
Eric J.
  • 139,555
  • 58
  • 313
  • 529
  • 11
    This looks like the correct approach to me - random passwords, salts, entropy and so forth should not be generated using Random() which is optimized for speed and generates reproducible sequences of numbers; RNGCryptoServiceProvider.GetNonZeroBytes() on the other hand produces wild sequences of numbers that are NOT reproducible. – mindplay.dk Apr 18 '11 at 20:09
  • 26
    The letters are slightly biased(255 % 62 != 0). Despite this minor flaw, it is by far the best solution here. – CodesInChaos Mar 17 '12 at 20:52
  • 13
    Note that this is *not* sound if you want crypto-strength, unbiased randomness. (And if you don't want that then why use `RNGCSP` in the first place?) Using mod to index into the `chars` array means that you'll get biased output unless `chars.Length` happens to be a divisor of 256. – LukeH Apr 03 '12 at 16:35
  • 15
    One possibility to reduce the bias a lot, is requesting `4*maxSize` random bytes, then use `(UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length`. I'd also use `GetBytes` instead of `GetNonZeroBytes`. And finally you can remove the first call to `GetNonZeroBytes`. You're not using its result. – CodesInChaos Apr 04 '12 at 08:19
  • Thanks Eric J. @CodesInChaos, why would you cast to UInt32 instead of just calling BitConvert.ToUInt32? – Nicholi Nov 06 '12 at 20:16
  • @Nicholi Probably because I didn't think of it at the time I wrote the comment. Now I'd prefer `BitConverter.ToUInt32`. – CodesInChaos Nov 06 '12 at 20:22
  • 1
    Why should we use `byte[] data = new byte[1];`, i used this answer without that and it worked. – Amir May 10 '13 at 13:16
  • @Amir: `The length of the byte array determines how many cryptographically strong random nonzero bytes are produced.` http://msdn.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider.getnonzerobytes.aspx. I'm not sure what the behavior is if the byte array is uninitialized. – Eric J. May 10 '13 at 21:48
  • 4
    Amir is right, `byte[] data = new byte[1]; ... crypto.GetNonZeroBytes(data);` those two lines have absolutely no effect. You're generating an array with 1 random byte, and then you never look at it. `new byte[length]` replaces the array with a different instance and everything you did to `data` before that point is discared. – CodesInChaos Oct 21 '13 at 10:17
  • 4
    @pseudonym27: Yes. The variable name `maxSize` should probably be called `size`. The password will be the size specified in that variable, not "up to" that size. – Eric J. Nov 17 '14 at 21:10
  • @EricJ.: How can i generate these numbers always start with alphabet ? – Amit Kumar Mar 31 '15 at 12:03
  • @AmitKumar: Add a boolean parameter `GetUniqueKey(int maxSize, bool alphaOnly)`, then set `chars` based on `alphaOnly`. Call once to get the first position `GetUniqueKey(1, true)`, then call a second time to get the rest of the positions `GetUniqueKey(numberOfAdditionalDigits, false)`. Combine the two calls. – Eric J. Mar 31 '15 at 16:17
  • 1
    As a side note, the character array can be `const string chars = "...";`, no need to call ToCharArray() on it. Changing this is seamless unless you've implemented @CodesInChaos suggestion for reducing bias, and then you may need to cast your index from a `long` back to an `int` (and be careful to cast the *result* of the modulo, and not the `uint` dividend). – Dusty Aug 12 '15 at 19:41
  • I'm no statistician but what if the data values from 249 - 256 were simply dropped? Would there still be a bias? – bkqc Apr 11 '16 at 20:33
  • @bkqc: You would remove bias, but also reduce the effective number of bits in the random string. If there are effectively fewer bits, the random string is weaker. If the random string is exposed (e.g. as part of a URL), it will become readily apparent that some bit positions are always 0. – Eric J. Apr 12 '16 at 15:12
  • Am I missing something here because I'm getting an error telling me I can't wrap RNGCryptoServiceProvider in a using statement because it doesn't implement IDisposable. – user609926 Jul 22 '16 at 13:34
  • @user609926: It does implement IDisposable. Do you have the correct implementation from System.Security.Cryptography? https://msdn.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider(v=vs.110).aspx – Eric J. Jul 23 '16 at 04:56
  • @EricJ.I'm not sure I understand... 0-255 is an 8 bit range. Not using 249-255 would remove 11111001 11111010 11111011 11111100 11111101 11111110 11111111 but 247 and 248 are 11110111 11111000 which show that all bits are used at one point or another. – bkqc Aug 05 '16 at 18:12
  • 2
    @bkqc: It's not a question of whether individual bits are set at some point, but a question of the set of possible values. If a hacker is aware that the values 249-255 are unused, it simplifies a brute-force hacking strategy. It's like if I tell you "I'm thinking of a number from one to ten, you try and guess it. Oh, and it's not 8 or 9." That last part makes the game easier for you. – Eric J. Aug 05 '16 at 18:18
  • 14
    Fun fact: A-Z a-z 0-9 is 62 characters. People are pointing out letter bias because 256 % 62 != 0. YouTube's video IDs are A-Z a-z 0-9, as well as "-" and "_", which produces 64 possible characters, which divides into 256 evenly. Coincidence? I think not! :) – qJake Sep 26 '16 at 18:25
  • 6
    It's not necessary to use GetNonZeroBytes() here, in fact it's bad to, since this will bias the distribution away from the lowercase 'a'. Plain GetBytes() should be used instead. – p10ben Oct 25 '16 at 00:12
  • 4
    As other said, this answer is really a good one, except it is biased (not so slightly in fact) for two reasons: first 256 % 62 != 0, second you should use GetBytes. – Teejay Aug 01 '17 at 00:19
  • 7
    I've put all the recommendations suggested here, in this script: https://gist.github.com/diegojancic/9f78750f05550fa6039d2f6092e461e5 – Diego Jancic Nov 03 '17 at 14:57
  • 1
    Just so you also explore SecureString: `SecureString secureString = new SecureString(); for (int i = 0; i < data.Length; i++) { secureString.AppendChar(chars[data[i] % chars.Length]); } secureString.MakeReadOnly(); return secureString;` – tomasz_kajetan_stanczak Mar 22 '19 at 12:25
  • 1
    To avoid this bias in the future, I have [requested that the .NET standard library provide the necessary classes](https://github.com/dotnet/dotnet-api-docs/issues/2762). – Roland Illig Jul 16 '19 at 05:13
  • @RolandIllig Thanks for the poke. Updated the answer and provided a basic test demonstrating that the result isn't obviously non-random and a link to further reading on mathematical proofs of randomness. – Eric J. Jul 25 '19 at 06:36
  • @EricJ. why are we using a char array, instead of a simple string object? It has Length property, it has index operator, what's the deal here? Could you, or anyone shine some light? – SpiritBob Feb 19 '20 at 17:30
  • 1
    @SpiritBob: Sure they both have the same interface as far as the existing code uses it. Semantically, the data represents a bunch of characters and not a string. The char[] seems a better fit for the meaning of the data. I don't see any performance or other advantage. – Eric J. Mar 10 '20 at 21:48
  • As @Dusty pointed out, uint % int returns a long, which cannot be used as an array index. I put the modulo operation in parenthesis and add an int casting for its result before assignment to idx. – burkay Jun 18 '20 at 21:12
212

Solution 1 - largest 'range' with most flexible length

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

This solution has more range than using a GUID because a GUID has a couple of fixed bits that are always the same and therefore not random, for example the 13 character in hex is always "4" - at least in a version 6 GUID.

This solution also lets you generate a string of any length.

Solution 2 - One line of code - good for up to 22 characters

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

You can't generate strings as long as Solution 1 and the string doesn't have the same range due to fixed bits in GUID's, but in a lot of cases this will do the job.

Solution 3 - Slightly less code

Guid.NewGuid().ToString("n").Substring(0, 8);

Mostly keeping this here for historical purpose. It uses slightly less code, that though comes as the expense of having less range - because it uses hex instead of base64 it takes more characters to represent the same range compared the other solutions.

Which means more chance of collision - testing it with 100,000 iterations of 8 character strings generated one duplicate.

Luis Perez
  • 26,159
  • 10
  • 76
  • 77
Douglas
  • 2,306
  • 1
  • 15
  • 10
  • 22
    You actually generated a duplicate? Surprising at 5,316,911,983,139,663,491,615,228,241,121,400,000 possible combinations of GUIDs. – Alex Aug 28 '09 at 00:10
  • 1
    (The chance for that happening is 1.8807909613156600127499784595556e-27, or virtually 0) – Alex Aug 28 '09 at 00:11
  • 73
    @Alex: He's shortening the GUID to 8 characters, so the probability of collisions is much higher than that of GUIDs. – dtb Aug 28 '09 at 00:19
  • 24
    Nobody can appreciate this other than nerds :) Yes you are absolutely right, the 8 char limit makes a difference. – Alex Aug 28 '09 at 00:36
  • 31
    Guid.NewGuid().ToString("n") will keep the dashes out, no Replace() call needed. But it should be mentioned, GUIDs are only 0-9 and A-F. The number of combinations is "good enough," but nowhere close to what a *true* alphanumeric random string permits. The chances of collision are 1:4,294,967,296 -- the same as a random 32-bit integer. – richardtallent Aug 28 '09 at 00:40
  • 33
    1) GUIDs are designed to be unique, not random. While current versions of windows generate V4 GUIDs which are indeed random, that's not guaranteed. For example older versions of windows used V1 GUIDs, where your could would fail. 2) Just using hex characters reduces the quality of the random string significantly. From 47 to 32 bits. 3) People are underestimating the collision probability, since they give it for individual pairs. If you generate 100k 32 bit values, you probably have one collision among them. See Birthday problem. – CodesInChaos Nov 16 '12 at 12:33
  • 8
    Stop wasting GUIDs! We have only 5,316,911,983,139,663,491,615,228,241,121,400,000 of them! – C-F Jul 06 '16 at 05:05
80

Here's an example that I stole from Sam Allen example at Dot Net Perls

If you only need 8 characters, then use Path.GetRandomFileName() in the System.IO namespace. Sam says using the "Path.GetRandomFileName method here is sometimes superior, because it uses RNGCryptoServiceProvider for better randomness. However, it is limited to 11 random characters."

GetRandomFileName always returns a 12 character string with a period at the 9th character. So you'll need to strip the period (since that's not random) and then take 8 characters from the string. Actually, you could just take the first 8 characters and not worry about the period.

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

PS: thanks Sam

dtb
  • 198,715
  • 31
  • 379
  • 417
Adam Porad
  • 13,054
  • 2
  • 30
  • 54
  • 31
    This works well. I ran it through 100,000 iterations and never had a duplicate name. However, I **did** find several vulgar words (in English). Wouldn't have even thought of this except one of the first ones in the list had F*** in it . Just a heads up if you use this for something the user will see. – techturtle Oct 09 '12 at 22:35
  • 3
    @techturtle Thanks for the warning. I suppose there's a risk for vulgar words with any random string generation that uses all letters in the alphabet. – Adam Porad Oct 10 '12 at 23:38
  • nice and simple but not good for long string ... vote for this good trick – Maher Abuthraa Feb 28 '14 at 09:55
  • This method seems to return only lower case alphanumeric strings. – jaybro Jun 05 '14 at 14:30
  • I don't understand the point of `.Replace(".", "")` being included in the operation chain when you are only taking the first 8 characters of the file name. This method seems to always generate 8 character file names, so there should never be a `"."` in that. Is that not correct? – crush Apr 02 '18 at 20:47
  • 4
    There are vulgar words from time to time, but if you keep this running long enough eventually it writes Shakespeare. (Just a few lifetimes of the universe. :) – Slothario Apr 30 '18 at 17:12
  • `Path.GetRandomFileName().Replace(".", "").Substring(0);` one-liner , I don't care about the length , only dependency is System.IO , well played . – LostNomad311 Aug 24 '18 at 19:00
  • @crush you're correct. Removing the period/`.` character is not necessary if 8 or less characters are needed. I mentioned that in the last sentence of the answer. I just in case someone wanted to use answer to generate more than 8 characters and I wanted to make sure they were aware that removing the period is needed. – Adam Porad Dec 26 '18 at 20:32
  • What's the point of the `GetRandomFileName` method? I've used `Path` all the time and never noticed it, seems like a completely useless method – Joel Mar 22 '21 at 22:26
39

The main goals of my code are:

  1. The distribution of strings is almost uniform (don't care about minor deviations, as long as they're small)
  2. It outputs more than a few billion strings for each argument set. Generating an 8 character string (~47 bits of entropy) is meaningless if your PRNG only generates 2 billion (31 bits of entropy) different values.
  3. It's secure, since I expect people to use this for passwords or other security tokens.

The first property is achieved by taking a 64 bit value modulo the alphabet size. For small alphabets (such as the 62 characters from the question) this leads to negligible bias. The second and third property are achieved by using RNGCryptoServiceProvider instead of System.Random.

using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}
piojo
  • 5,023
  • 1
  • 19
  • 30
CodesInChaos
  • 100,017
  • 20
  • 197
  • 251
  • 1
    There is no intersection with 64 x Z and Math.Pow(2,Y). So while making bigger numbers reduces the bias, it doesn't eliminate it. I updated my answer down below, my approach was to discard random inputs and replace with another value. – Todd Feb 20 '16 at 10:00
  • @Todd I know that it doesn't eliminate the bias, but I chose the simplicity of this solution over eliminating a practically irrelevant bias. – CodesInChaos Feb 20 '16 at 10:32
  • I agree for most cases it's probably practically irrelevant. But now I've updated mine to be both as fast as Random and a little more secure than yours. All open source for all to share. Yes, I wasted way too much time on this... – Todd Feb 20 '16 at 12:06
  • If we are using RNG provider do we have any way to avoid bias in theory? I am not sure... If Todd mean the way when he generates additional random number (when we are in bias zone) then it can be wrong assumption. RNG has almost linear distribution of all generated values in average. But it does not mean that we will not have local correlation between generated bytes. So additional byte only for bias zone can still give us some bias but due to different reason. Most likely this bias will be very small. BUT in this case increasing of total generated bytes is more straightforward way. – Maxim Jul 31 '17 at 16:18
  • 1
    @Maxim You can use rejection to completely eliminate the bias (assuming the underlying generator is perfectly random). In exchange the code might run arbitrarily long (with exponentially small probability). – CodesInChaos Jul 31 '17 at 17:56
  • I have questions. I guess you mean this (https://stackoverflow.com/questions/32258979/eliminating-modulo-bias-how-is-it-achieved-in-the-arc4random-uniform-function). It is similar to Todd solution. But RNG provider should output independent data for each GetBytes (including one for rejection iteration). Should it be at least new instance of RNG provider? Todd uses shared random buffer (static byte[] byteCache = new byte[1000000]). I think it will introduce local correlation. Or maybe I am wrong and EACH BYTE in RNG is perfectly random? In any case "why code might run arbitrarily long"? – Maxim Jul 31 '17 at 19:27
  • @Maxim If the bytes are perfectly random, why might hit the rejection case over and over again, but the odds of that happening decrease exponentially with the number of tries. | The design goal of a stream cipher/PRNG is to generate huge amounts of outputs that are indistinguishible from true random data from a small (say 256-bit) seed. Cryptographers are pretty good at building this kind of high quality PRNG, so for my answer I'm just assuming that the output of `RNGCryptoServiceProvider` behaves like true random numbers. – CodesInChaos Jul 31 '17 at 21:18
34

The simplest:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

You can get better performance if you hard code the char array and rely on System.Random:

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

If ever you worry the English alphabets can change sometime around and you might lose business, then you can avoid hard coding, but should perform slightly worse (comparable to Path.GetRandomFileName approach)

public static string GetRandomAlphaNumeric()
{
    var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

public static IEnumerable<char> To(this char start, char end)
{
    if (end < start)
        throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
    return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}

The last two approaches looks better if you can make them an extension method on System.Random instance.

nawfal
  • 62,042
  • 48
  • 302
  • 339
  • 1
    Using `chars.Select` is a big ugly since it relies on the output size being at most the alphabet size. – CodesInChaos Jun 04 '14 at 10:09
  • @CodesInChaos I'm not sure if I understand you. You mean in the `'a'.To('z')` approach? – nawfal Jun 04 '14 at 10:18
  • 1
    1) `chars.Select()`.Take(n)` only works if `chars.Count >= n`. Selecting on a sequence you don't actually use is a bit unintuitive, especially with that implicit length constraint. I'd rather use `Enumerable.Range` or `Enumerable.Repeat`. 2) The error message "the end char should be less than start char" is the wrong way round/missing a `not`. – CodesInChaos Jun 04 '14 at 10:20
  • @CodesInChaos but in my case `chars.Count` is way `> n`. Also I dont get the unintuitive part. That does make all uses `Take` unintuitive doesnt it? I dont believe it. Thanks for pointing the typo. – nawfal Jun 04 '14 at 10:34
  • 5
    This is featured on [theDailyWTF.com](https://thedailywtf.com/articles/ten-times-as-unique) as a CodeSOD article. –  Oct 03 '18 at 05:33
  • I only want it for unit testing. This is perfect! – CindyH Apr 27 '21 at 22:02
22

Just some performance comparisons of the various answers in this thread:

Methods & Setup

// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();


// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// http://stackoverflow.com/a/1344258/1037948
public string ForLoop(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
    }
    return new string(result);
}

public string ForLoopNonOptimized(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleChars[random.Next(possibleChars.Length)];
    }
    return new string(result);
}

public string Repeat(int num) {
    return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}

// http://stackoverflow.com/a/1518495/1037948
public string GenerateRandomString(int num) {
  var rBytes = new byte[num];
  random.NextBytes(rBytes);
  var rName = new char[num];
  while(num-- > 0)
    rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
  return new string(rName);
}

//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; 
    char[] rName = new char[Length];
    SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Results

Tested in LinqPad. For string size of 10, generates:

  • from Linq = chdgmevhcy [10]
  • from Loop = gtnoaryhxr [10]
  • from Select = rsndbztyby [10]
  • from GenerateRandomString = owyefjjakj [10]
  • from SecureFastRandom = VzougLYHYP [10]
  • from SecureFastRandom-NoCache = oVQXNGmO1S [10]

And the performance numbers tend to vary slightly, very occasionally NonOptimized is actually faster, and sometimes ForLoop and GenerateRandomString switch who's in the lead.

  • LinqIsTheNewBlack (10000x) = 96762 ticks elapsed (9.6762 ms)
  • ForLoop (10000x) = 28970 ticks elapsed (2.897 ms)
  • ForLoopNonOptimized (10000x) = 33336 ticks elapsed (3.3336 ms)
  • Repeat (10000x) = 78547 ticks elapsed (7.8547 ms)
  • GenerateRandomString (10000x) = 27416 ticks elapsed (2.7416 ms)
  • SecureFastRandom (10000x) = 13176 ticks elapsed (5ms) lowest [Different machine]
  • SecureFastRandom-NoCache (10000x) = 39541 ticks elapsed (17ms) lowest [Different machine]
Todd
  • 14,946
  • 6
  • 42
  • 56
drzaus
  • 21,536
  • 14
  • 123
  • 183
  • 4
    Would be interesting to know which ones created dupes. – Rebecca Aug 01 '13 at 12:43
  • @Junto -- to figure out which results in duplicates, something like `var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());`, where you replace `EachRandomizingMethod` with...each method – drzaus Sep 11 '13 at 17:06
20

One line of code Membership.GeneratePassword() does the trick :)

Here is a demo for the same.

Stephen Kennedy
  • 16,598
  • 21
  • 82
  • 98
Pooran
  • 1,398
  • 2
  • 15
  • 23
  • 1
    Looks like Microsoft moved the link.. another code sample is at https://msdn.microsoft.com/en-us/library/ms152017 or http://aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx or http://developer.xamarin.com/api/member/System.Web.Security.Membership.GeneratePassword(System.Int32,System.Int32)/ – Pooran Aug 02 '15 at 18:26
  • I thought of that but couldn't get rid of the non alphanumeric characters as the 2nd argument is the MINIMUM non alpha characters – Developer1010 Apr 05 '18 at 10:29
14

The code written by Eric J. is quite sloppy (it is quite clear that it is from 6 years ago... he probably wouldn't write that code today), and there are even some problems.

Unlike some of the alternatives presented, this one is cryptographically sound.

Untrue... There is a bias in the password (as written in a comment), bcdefgh are a little more probable than the others (the a isn't because by the GetNonZeroBytes it isn't generating bytes with a value of zero, so the bias for the a is balanced by it), so it isn't really cryptographically sound.

This should correct all the problems.

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}
xanatos
  • 102,557
  • 10
  • 176
  • 249
9

My simple one line code works for me :)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

To expand on this for any length string

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }
Developer1010
  • 1,779
  • 1
  • 23
  • 24
Raj kumar
  • 995
  • 11
  • 17
7

We also use custom string random but we implemented is as a string's helper so it provides some flexibility...

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

Usage

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

or

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
Mr. Pumpkin
  • 5,427
  • 4
  • 38
  • 51
6

After reviewing the other answers and considering CodeInChaos' comments, along with CodeInChaos still biased (although less) answer, I thought a final ultimate cut and paste solution was needed. So while updating my answer I decided to go all out.

For an up to date version of this code, please visit the new Hg repository on Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom. I recommend you copy and paste the code from: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (make sure you click the Raw button to make it easier to copy and make sure you have the latest version, I think this link goes to a specific version of the code, not the latest).

Updated notes:

  1. Relating to some other answers - If you know the length of the output, you don't need a StringBuilder, and when using ToCharArray, this creates and fills the array (you don't need to create an empty array first)
  2. Relating to some other answers - You should use NextBytes, rather than getting one at a time for performance
  3. Technically you could pin the byte array for faster access.. it's usually worth it when your iterating more than 6-8 times over a byte array. (Not done here)
  4. Use of RNGCryptoServiceProvider for best randomness
  5. Use of caching of a 1MB buffer of random data - benchmarking shows cached single bytes access speed is ~1000x faster - taking 9ms over 1MB vs 989ms for uncached.
  6. Optimised rejection of bias zone within my new class.

End solution to question:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

But you need my new (untested) class:

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

For history - my older solution for this answer, used Random object:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

Performance:

  1. SecureFastRandom - First single run = ~9-33ms. Imperceptible. Ongoing: 5ms (sometimes it goes up to 13ms) over 10,000 iterations, With a single average iteration= 1.5 microseconds.. Note: Requires generally 2, but occasionally up to 8 cache refreshes - depends on how many single bytes exceed the bias zone
  2. Random - First single run = ~0-1ms. Imperceptible. Ongoing: 5ms over 10,000 iterations. With a single average iteration= .5 microseconds.. About the same speed.

Also check out:

These links are another approach. Buffering could be added to this new code base, but most important was exploring different approaches to removing bias, and benchmarking the speeds and pros/cons.

Todd
  • 14,946
  • 6
  • 42
  • 56
  • I found some slight performance enhancements to your method, which seemed the fasted of the bunch -- http://stackoverflow.com/a/17092645/1037948 – drzaus Jun 13 '13 at 16:46
  • 5
    1) Why all those magic constants? You specified the output length *three* times. Just define it as a constant or a parameter. You can use `charSet.Length` instead of `62`. 2) A static `Random` without locking means this code is not threadsafe. 3) reducing 0-255 mod 62 introduces a detectable bias. 4) You can't use `ToString` on a char array, that always returns `"System.Char[]"`. You need to use `new String(rName)` instead. – CodesInChaos Jun 04 '14 at 10:04
  • Thanks @CodesInChaos, I never thought of those things back then. Still only using Random class, but this should be better. I couldn't think of any better way to detect and correct bias inputs. – Todd Feb 20 '16 at 09:44
  • It's a bit silly to start with a weak RNG (`System.Random`) and then carefully avoiding any bias in your own code. The expression "polishing a turd" comes to mind. – CodesInChaos Feb 20 '16 at 11:03
  • @CodesInChaos And now the apprentice has surpassed his master – Todd Feb 20 '16 at 12:02
  • From what I remember caching `RNGCryptoService` has diminishing returns at about 10kB, 1MB seems like overkill to me. I think for my RNG library I chose to 8 KiB as cache size. – CodesInChaos Feb 20 '16 at 12:07
  • Correct, that's what I saw. Sort of: Over 1000 iterations - 10kB = 14ms. 100kB = 140-169ms, 1MB = 1682-1762ms. I chose 1MB because it was ~1-2ms per single iteration - still imperceptible, but minimizes the amount of calls. 1MB is easily stored. And anyone can reduce that number if they like. – Todd Feb 20 '16 at 12:18
  • Oh, and just to point out - the main motivation for caching was because my bias elimination algorithm needed access to random data one byte at a time - 1. RNG... doesn't have a single byte method (a minor inconvenience), 2. Any GetBytes initiation overhead is multiplied with singlebyte access. Overall this means a ~1000x performance improvement for ongoing use which is significant. – Todd Feb 20 '16 at 12:23
  • Maybe I'm missing something? (I'm no expert at C#), but where is "SecureFastRandom.GetNextBytesMax"? And your depo is private (Access denied). – bshea Oct 07 '16 at 04:37
  • @bshea My bad. Now it's open. – Todd Jul 15 '17 at 12:44
6

Question: Why should I waste my time using Enumerable.Range instead of typing in "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"?

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var randomCharacters = GetRandomCharacters(8, true);
        Console.WriteLine(new string(randomCharacters.ToArray()));
    }

    private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
    {
        var integers = Enumerable.Empty<int>();
        integers = integers.Concat(Enumerable.Range('A', 26));
        integers = integers.Concat(Enumerable.Range('0', 10));

        if ( includeLowerCase )
            integers = integers.Concat(Enumerable.Range('a', 26));

        return integers.Select(i => (char)i).ToList();
    }

    public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
    {
        var characters = getAvailableRandomCharacters(includeLowerCase);
        var random = new Random();
        var result = Enumerable.Range(0, count)
            .Select(_ => characters[random.Next(characters.Count)]);

        return result;
    }
}

Answer: Magic strings are BAD. Did ANYONE notice there was no "I" in my string at the top? My mother taught me not to use magic strings for this very reason...

n.b. 1: As many others like @dtb said, don't use System.Random if you need cryptographic security...

n.b. 2: This answer isn't the most efficient or shortest, but I wanted the space to separate the answer from the question. The purpose of my answer is more to warn against magic strings than to provide a fancy innovative answer.

Wai Ha Lee
  • 7,664
  • 52
  • 54
  • 80
  • Why do I care that there's no "`I`?" – Christine Dec 12 '16 at 19:01
  • 1
    Alphanumeric (ignoring case) is `[A-Z0-9]`. If, by accident, your random string only ever covers `[A-HJ-Z0-9]` the result doesn't cover the full allowable range, which may be problematic. – Wai Ha Lee Dec 13 '16 at 16:14
  • How would that be problematic? So it doesn't contain `I`. Is it because there's one less character and that makes it easier to crack? What are the statistics on crackable passwords that contain 35 characters in the range than 36. I think I would rather risk it... or just proof reed the character range... than include all that extra garbage in my code. But, that's me. I mean, not to be a butt hole, I'm just saying. Sometimes I think programmers have a tendency go the extra-complex route for the sake of being extra-complex. – Christine Dec 13 '16 at 16:22
  • 1
    It deepends on the use case. It's very common to exclude characters such as `I` and `O` from these types of random strings to avoid humans confusing them with `1` and `0`. If you don't care about having a human-readable string, fine, but if it's something someone might need to type, then it's actually smart to remove those characters. – Chris Pratt Aug 22 '17 at 03:04
6
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
Tejas
  • 417
  • 5
  • 12
6

Another option could be to use Linq and aggregate random chars into a stringbuilder.

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());
AAD
  • 281
  • 3
  • 9
5

A slightly cleaner version of DTB's solution.

    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
    return string.Join("", list);

Your style preferences may vary.

Rob Deary
  • 857
  • 10
  • 3
4

Try to combine two parts: unique (sequence, counter or date ) and random

public class RandomStringGenerator
{
    public static string Gen()
    {
        return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
    }

    private static string GenRandomStrings(int strLen)
    {
        var result = string.Empty;

        using (var gen = new RNGCryptoServiceProvider())
        {
            var data = new byte[1];

            while (result.Length < strLen)
            {
                gen.GetNonZeroBytes(data);
                int code = data[0];
                if (code > 48 && code < 57 || // 0-9
                    code > 65 && code < 90 || // A-Z
                    code > 97 && code < 122   // a-z
                )
                {
                    result += Convert.ToChar(code);
                }
            }

            return result;
        }
    }

    private static string ConvertToBase(long num, int nbase = 36)
    {
        const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish to make the algorithm more secure - change order of letter here

        // check if we can convert to another base
        if (nbase < 2 || nbase > chars.Length)
            return null;

        int r;
        var newNumber = string.Empty;

        // in r we have the offset of the char that was converted to the new base
        while (num >= nbase)
        {
            r = (int)(num % nbase);
            newNumber = chars[r] + newNumber;
            num = num / nbase;
        }
        // the last number to convert
        newNumber = chars[(int)num] + newNumber;

        return newNumber;
    }
}

Tests:

    [Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }
Mohsen Esmailpour
  • 9,428
  • 1
  • 37
  • 60
RouR
  • 5,300
  • 3
  • 27
  • 23
  • 1) You can use character literals instead of the ASCII values associated with those characters. 2) You have an off-by-one mistake in your interval matching code. You need to use `<=` and `>=` instead of ``. 3) I'd add the unnecessary parentheses around the `&&` expressions to make it clear they have precedence, but of course that's only a stylistic choice. – CodesInChaos Oct 24 '16 at 16:58
  • + 1 Good for removing bias and adding testing. I'm not sure why you prepend your random string with a timestamp-derived string though? Also, you still need to dispose your RNGCryptoServiceProvider – monty Jun 07 '18 at 22:11
4

I was looking for a more specific answer, where I want to control the format of the random string and came across this post. For example: license plates (of cars) have a specific format (per country) and I wanted to created random license plates.
I decided to write my own extension method of Random for this. (this is in order to reuse the same Random object, as you could have doubles in multi-threading scenarios). I created a gist (https://gist.github.com/SamVanhoutte/808845ca78b9c041e928), but will also copy the extension class here:

void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}
Sam Vanhoutte
  • 2,424
  • 13
  • 29
4

Now in one-liner flavour.

private string RandomName()
{
        return new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    using (var cryptoProvider = new RNGCryptoServiceProvider())
                        cryptoProvider.GetBytes(cryptoResult);

                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());
}
Matas Vaitkevicius
  • 49,230
  • 25
  • 212
  • 228
3

Horrible, I know, but I just couldn't help myself:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}

james
  • 832
  • 5
  • 7
2

A solution without using Random :

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
w.b
  • 10,250
  • 5
  • 25
  • 44
2

Here is a variant of Eric J's solution, i.e. cryptographically sound, for WinRT (Windows Store App):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new StringBuilder(length);
    for (int i = 0; i < length; ++i)
    {
        result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
    }
    return result.ToString();
}

If performance matters (especially when length is high):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}
huyc
  • 187
  • 1
  • 8
  • 1
    This is *not* cryptographically sound. There is a small bias due to the modulus operation not spreading the entire width of ulong equally into 62 chars. – Lie Ryan May 30 '17 at 15:24
2

For both crypto & noncrypto, efficiently:

public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new Random().GenerateRandomString(length, charset);

public static string GenerateRandomString(this Random random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    RandomString(random.NextBytes, length, charset.ToCharArray());

public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
        return crypto.GenerateRandomCryptoString(length, charset);
}

public static string GenerateRandomCryptoString(this RNGCryptoServiceProvider random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") => 
    RandomString(random.GetBytes, length, charset.ToCharArray());

private static string RandomString(Action<byte[]> fillRandomBuffer, int length, char[] charset)
{
    if (length < 0)
        throw new ArgumentOutOfRangeException(nameof(length), $"{nameof(length)} must be greater or equal to 0");
    if (charset is null)
        throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0)
        throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    var maxIdx = charset.Length;
    var chars = new char[length];
    var randomBuffer = new byte[length * 4];
    fillRandomBuffer(randomBuffer);

    for (var i = 0; i < length; i++)
        chars[i] = charset[BitConverter.ToUInt32(randomBuffer, i * 4) % maxIdx];

    return new string(chars);
}

Using generators & LINQ. Not the fastest option (especially because it doesn't generate all the bytes in one go) but pretty neat & extensible:

private static readonly Random _random = new Random();

public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new string(_random.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());

public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
        return new string(crypto.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
}

public static IEnumerable<char> RandomChars(this Func<uint, IEnumerable<uint>> randomGenerator, char[] charset)
{
    if (charset is null)
        throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0)
        throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    return randomGenerator((uint)charset.Length).Select(r => charset[r]);
}

public static Func<uint, IEnumerable<uint>> GetGenerator(this Random random)
{
    if (random is null)
        throw new ArgumentNullException(nameof(random));

    return GeneratorFunc_Inner;

    IEnumerable<uint> GeneratorFunc_Inner(uint maxValue)
    {
        if (maxValue > int.MaxValue)
            throw new ArgumentOutOfRangeException(nameof(maxValue));

        return Generator_Inner();

        IEnumerable<uint> Generator_Inner()
        {
            var randomBytes = new byte[4];
            while (true)
            {
                random.NextBytes(randomBytes);
                yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
            }
        }
    }
}

public static Func<uint, IEnumerable<uint>> GetGenerator(this System.Security.Cryptography.RNGCryptoServiceProvider random)
{
    if (random is null)
        throw new ArgumentNullException(nameof(random));

    return Generator_Inner;

    IEnumerable<uint> Generator_Inner(uint maxValue)
    {
        var randomBytes = new byte[4];
        while (true)
        {
            random.GetBytes(randomBytes);
            yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
        }
    }
}

a simpler version using LINQ for only non-crypto strings:

private static readonly Random _random = new Random();

public static string RandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
    new string(_random.GenerateChars(charset).Take(length).ToArray()); 

public static IEnumerable<char> GenerateChars(this Random random, string charset)
{
    if (charset is null) throw new ArgumentNullException(nameof(charset));
    if (charset.Length == 0) throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));

    return random.Generator(charset.Length).Select(r => charset[r]);
}

public static IEnumerable<int> Generator(this Random random, int maxValue)
{
    if (random is null) throw new ArgumentNullException(nameof(random));

    return Generator_Inner();

    IEnumerable<int> Generator_Inner() { while (true) yield return random.Next(maxValue); }
}
Jake Walden
  • 275
  • 2
  • 4
1

I know this one is not the best way. But you can try this.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
Sagar
  • 5,423
  • 5
  • 18
  • 34
  • 2
    How is that one line? Console.WriteLine($"Random string: {Path.GetRandomFileName().Replace(".","")}"); is one line. – PmanAce May 23 '18 at 15:02
1

I don't know how cryptographically sound this is, but it's more readable and concise than the more intricate solutions by far (imo), and it should be more "random" than System.Random-based solutions.

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

I can't decide if I think this version or the next one is "prettier", but they give the exact same results:

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

Granted, it isn't optimized for speed, so if it's mission critical to generate millions of random strings every second, try another one!

NOTE: This solution doesn't allow for repetitions of symbols in the alphabet, and the alphabet MUST be of equal or greater size than the output string, making this approach less desirable in some circumstances, it all depends on your use-case.

sara
  • 3,203
  • 11
  • 26
0
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
KregHEk
  • 402
  • 4
  • 11
  • 2
    1) static methods should be thread safe. 2) What's the point of incrementing the index instead of using the result of `random.Next` directly? Complicates the code and doesn't achieve anything useful. – CodesInChaos Jun 04 '14 at 10:01
0

If your values are not completely random, but in fact may depend on something - you may compute an md5 or sha1 hash of that 'somwthing' and then truncate it to whatever length you want.

Also you may generate and truncate a guid.

Alexey B.
  • 968
  • 7
  • 15
0

Here is a mechanism to generate a random alpha-numeric string (I use this to generate passwords and test data) without defining the alphabet and numbers,

CleanupBase64 will remove necessary parts in the string and keep adding random alpha-numeric letters recursively.

        public static string GenerateRandomString(int length)
        {
            var numArray = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(numArray);
            return CleanUpBase64String(Convert.ToBase64String(numArray), length);
        }

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }
Dhanuka777
  • 7,389
  • 5
  • 61
  • 112
  • You have declared `GenerateRandomString` and make a call to `GetRandomString` from within `SanitiseBase64String`. Also you have declared `SanitiseBase64String` and call `CleanUpBase64String` in `GenerateRandomString`. – Wai Ha Lee Sep 30 '15 at 02:53
0

There is one of the awasome nuget packages that make this so simple.

var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();

One of the good example is here.

nzrytmn
  • 3,653
  • 27
  • 23
0

not 100% sure, as I didn't test EVERY option here, but of the ones I did test, this one is the fastest. timed it with stopwatch and it showed 9-10 ticks so if speed is more important than security, try this:

 private static Random random = new Random(); 
 public static string Random(int length)
     {   
          var stringChars = new char[length];

          for (int i = 0; i < length; i++)
              {
                  stringChars[i] = (char)random.Next(0x30, 0x7a);                  
                  return new string(stringChars);
              }
     }
PetNoire
  • 49
  • 4
  • Why reply ? This question has been answered a lot of times and yet you still post your half-working answer.. – Laurent May 24 '20 at 16:17
-1

Very simple solution. It uses ASCII values and just generates "random" characters in between them.

public static class UsernameTools
{
    public static string GenerateRandomUsername(int length = 10)
    {
        Random random = new Random();
        StringBuilder sbuilder = new StringBuilder();
        for (int x = 0; x < length; ++x)
        {
            sbuilder.Append((char)random.Next(33, 126));
        }
        return sbuilder.ToString();
    }

}
Serguei Fedorov
  • 6,947
  • 9
  • 55
  • 86
-3

You just use the assembly SRVTextToImage. And write below code to generate random string.

CaptchaRandomImage c1 = new CaptchaRandomImage();
            string text = c1.GetRandomString(8);

Mostly it is used to implement the Captcha. But in your case it also works. Hope it helps.

Salman Mushtaq
  • 333
  • 3
  • 19
  • So you're saying we should use some 3rd party library originally designed to generate CAPTCHAs to generate a random string? – Tsahi Asher Jan 10 '17 at 16:30
  • 3
    The string isn't the only thing that's random about this suggestion! i love it, definately the most original! – BanksySan Jan 12 '17 at 18:27