If you generate two passwords very quickly they will be generated on the same tick.
If you just want to generate a random human readable password, look here. If you want to know why Random
is not good for this purpose and how you might do something more appropriate read on.
The quickest thing to do is use the default constructor of Random()
, that will do the seeding for you.
After checking the documentation, the default constructor uses a time based seed, so you'd suffer the same issues with its use. Anyway, the Random
class is too predictable to use for safe password generation.
If you are looking for a little more strength you could do this,
using System.Security.Cryptography;
static string GetPassword(int length = 13)
{
var rng = new RNGCryptoServiceProvider();
var buffer = new byte[length * sizeof(char)];
rng.GetNonZeroBytes(buffer);
return new string(Encoding.Unicode.GetChars(buffer));
}
However, if you want humans to be able to read, remember and type your generated passwords you should be a little more limited in your range of possible characters.
I've updated this part to give a detailed, modern, unbiased answer.
If you want to limit output to a certain set of chars you can do something like this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
/// <summary>
/// Get a random password.
/// </summary>
/// <param name="valid">A list of valid password chars.</param>
/// <param name="length">The length of the password.</returns>
/// <returns>A random password.</returns>
public static string GetPassword(IList<char> valid, int length = 13)
{
return new string(GetRandomSelection(valid, length).ToArray());
}
/// <summary>
/// Gets a random selection from <paramref name="valid"/>.
/// </summary>
/// <typeparam name="T">The item type.</typeparam>
/// <param name="valid">List of valid possibilities.</param>
/// <param name="length">The length of the result sequence.</param>
/// <returns>A random sequence</returns>
private static IEnumerable<T> GetRandomSelection<T>(
IList<T> valid,
int length)
{
// The largest multiple of valid.Count less than ulong.MaxValue.
// This upper limit prevents bias in the results.
var max = ulong.MaxValue - (ulong.MaxValue % (ulong)valid.Count);
// A finite sequence of random ulongs.
var ulongs = RandomUInt64Sequence(max, length).Take(length);
// A sequence of indecies.
var indecies = ulongs.Select((u => (int)(u % (ulong)valid.Count)));
return indecies.Select(i => valid[i]);
}
/// <summary>
/// An infinite sequence of random <see cref="ulong"/>s.
/// </summary>
/// <param name="max">
/// The maximum inclusive <see cref="ulong"/> to return.
/// </param>
/// <param name="poolSize">
/// The size, in <see cref="ulong"/>s, of the pool used to
/// optimize <see cref="RNGCryptoServiceProvider"/> calls.
/// </param>
/// <returns>A random <see cref="ulong"/> sequence.</returns>
private static IEnumerable<ulong RandomUInt64Sequence(
ulong max = UInt64.MaxValue,
int poolSize = 100)
{
var rng = new RNGCryptoServiceProvider();
var pool = new byte[poolSize * sizeof(ulong)];
while (true)
{
rng.GetBytes(pool);
for (var i = 0; i < poolSize; i++)
{
var candidate = BitConvertor.ToUInt64(pool, i * sizeof(ulong));
if (candidate > max)
{
continue;
}
yield return candidate;
}
}
}
You can use this code like this, first you need a set of valid char
a that could be in your password,
var validChars = new[] { 'A', 'B', 'C' };
for ilustration i've included just 3 char
s, in practice you'd want many more char
s to be included. Then, to genrate a random password 8 char
s long you make this call.
var randomPassword = GetPassword(validChars, 8);
In practice, you probably want your passwords to be at least 13 char
s.