C# random string generator

Generate one here, or copy the C# below. Every example uses RandomNumberGenerator and was run on .NET 10.

G
More options
up to 10,000

Constraints

The short answer

On .NET 8 or later, call RandomNumberGenerator.GetString(alphabet, length). It draws from the operating system CSPRNG and chooses every character uniformly, in one line.

On .NET Core 3.0 through .NET 7, call RandomNumberGenerator.GetInt32(alphabet.Length) for each character and build the string yourself. GetInt32 rejects out-of-range values internally, so there is no modulo bias.

On .NET Framework, neither method exists. Fill a byte array from RandomNumberGenerator.Create() and apply rejection sampling yourself, discarding any byte at or above the largest multiple of the alphabet size that fits in a byte.

RandomNumberGenerator helpers and when they arrived
MethodAvailable fromReturns
GetString(choices, length).NET 8A string of uniformly chosen characters
GetHexString(length, lowercase).NET 8Hex digits, uppercase by default
GetItems(choices, length).NET 8An array of uniformly chosen elements
GetInt32(toExclusive).NET Core 3.0A uniform integer
GetBytes(count).NET 6A new array of random bytes

GetHexString returns uppercase hex unless you pass lowercase: true. Code that compares a stored lowercase token with a new uppercase one will fail in a way that looks like a bad token.

Why System.Random is not safe

System.Random is built for speed and repeatability. On .NET 10, inspecting a new Random() showed it uses the xoshiro implementation that .NET introduced in version 6, and new Random(42) switches to a separate legacy algorithm kept so that seeded sequences stay the same as on older runtimes. Random.Shared is the xoshiro version behind a thread-safe wrapper.

None of these are cryptographic. Xoshiro generators are linear, so their state can be worked out from their output, and a seeded Random repeats exactly on every run. On .NET Framework, new Random() seeds itself from the system tick count, so instances created within the same tick produce the same sequence.

What each API is, checked on .NET 10
APIImplementationSafe for secrets
new Random()XoshiroNo
new Random(seed)Legacy seeded algorithmNo
Random.SharedXoshiro, thread-safeNo
RandomNumberGeneratorOperating system CSPRNGYes

Mistakes that pass code review

Each of these compiles cleanly and produces output that looks right.

  • chars[bytes[i] % chars.Length] over bytes from RandomNumberGenerator. A secure source with a biased mapping: with 62 characters, the first eight are 25 percent more likely than the rest.
  • Guid.NewGuid().ToString("N").Substring(0, 8) as a short ID. Eight hex characters are 32 bits, and a collision becomes more likely than not after about 77,000 values.
  • Convert.ToBase64String(bytes) in a URL or query string. It emits plus, slash and equals. On .NET 9 and later use Base64Url.EncodeToString; on older versions replace the characters and trim the padding.
  • new RNGCryptoServiceProvider() in new code. It still works, but the type has been marked obsolete since .NET 6. Use the static RandomNumberGenerator methods.
  • new Random() created per request in a .NET Framework web application. Requests arriving within the same tick receive identical sequences, so two users can be issued the same value.

Many unique strings

Add strings to a HashSet<string> until it holds as many as you need. At 16 characters from 62 a collision is so unlikely that the loop almost never repeats. If the values are stored, add a unique index as well, because a set in one process cannot see values created by another.

Doing this in your own code

C# on .NET 8+ - GetString
using System;
using System.Security.Cryptography;

const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

for (int i = 0; i < 3; i++)
{
    // Uniform over Alphabet, drawn from the OS CSPRNG.
    Console.WriteLine(RandomNumberGenerator.GetString(Alphabet, 24));
}
C# on .NET 6 and 7 - GetInt32
using System;
using System.Security.Cryptography;

const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

static string RandomString(int length)
{
    var chars = new char[length];
    for (int i = 0; i < length; i++)
    {
        // GetInt32 rejects out-of-range values, so there is no modulo bias.
        chars[i] = Alphabet[RandomNumberGenerator.GetInt32(Alphabet.Length)];
    }
    return new string(chars);
}

for (int i = 0; i < 3; i++)
{
    Console.WriteLine(RandomString(24));
}

GetInt32 exists from .NET Core 3.0. The top-level program form shown here needs C# 9 or later.

C# on .NET 9+ - hex and URL-safe tokens
using System;
using System.Buffers.Text;
using System.Security.Cryptography;

byte[] bytes = RandomNumberGenerator.GetBytes(32);

Console.WriteLine(Convert.ToHexString(bytes).ToLowerInvariant());          // 64 hex characters
Console.WriteLine(Base64Url.EncodeToString(bytes));                        // 43 URL-safe characters
Console.WriteLine(RandomNumberGenerator.GetHexString(32, lowercase: true)); // 32 hex characters
C# on .NET 8+ - password with every character type
using System;
using System.Linq;
using System.Security.Cryptography;

string[] classes = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", "0123456789", "!#$%*+-=?@^_" };
string alphabet = string.Concat(classes);

string Password(int length)
{
    if (length < classes.Length) throw new ArgumentOutOfRangeException(nameof(length));
    while (true)
    {
        // Whole-string rejection keeps the result uniform over valid passwords.
        string candidate = RandomNumberGenerator.GetString(alphabet, length);
        if (classes.All(c => candidate.IndexOfAny(c.ToCharArray()) >= 0)) return candidate;
    }
}

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(Password(20));
}

Questions

How do I generate a random string in C#?

On .NET 8 and later, RandomNumberGenerator.GetString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 24). It is secure and uniform. On .NET Core 3.0 to .NET 7, pick each character with RandomNumberGenerator.GetInt32(alphabet.Length).

Is System.Random secure in .NET?

No. new Random() and Random.Shared use a xoshiro generator, and new Random(seed) uses a legacy algorithm, and none of them is cryptographic. Their output can be predicted, and a seeded instance repeats exactly. Use RandomNumberGenerator for passwords, tokens and keys.

How do I generate a random hex string in C#?

On .NET 8 and later, RandomNumberGenerator.GetHexString(32, lowercase: true) returns 32 lowercase hex digits. On .NET 5 and later, Convert.ToHexString(RandomNumberGenerator.GetBytes(16)) returns 32 uppercase digits from 16 random bytes.

How do I create a URL-safe random token in C#?

On .NET 9 and later, Base64Url.EncodeToString(RandomNumberGenerator.GetBytes(32)) returns 43 URL-safe characters carrying 256 bits. Base64Url lives in System.Buffers.Text, and the Microsoft.Bcl.Memory package brings it to older targets.

Can I use Guid.NewGuid() as a random string?

For uniqueness, yes: a version 4 GUID has 122 random bits. As a compact secret it is inefficient, since it spends 36 characters on hex digits, hyphens and fixed version bits. For a token, encode random bytes directly.

Related generators