/// <summary> /// Generates a password string with a specified length /// </summary> /// <param name="length">Length of the password that will be generated</param> /// <param name="generationFlags">Configuration for the password generation</param> /// <returns>The generated password</returns> public static string GeneratePassword(int length, PasswordGenerationFlags generationFlags = PasswordGenerationFlags.Default) { if (length <= 0) { throw new ArgumentOutOfRangeException(nameof(length)); } // Build character set string[] charSets = BuildCharacterSet(generationFlags).ToArray(); if (charSets.Length == 0) { throw new ArgumentException("No char sets selected.", nameof(generationFlags)); } if (length < charSets.Length) { throw new ArgumentException("Password length is too small to contain characters form all selected sets.", nameof(generationFlags)); } // Use cryptographically safe random number generator using (var randomNumberGenerator = RandomNumberGenerator.Create()) { // Generate random numbers var randomBytes = new byte[length]; randomNumberGenerator.GetBytes(randomBytes); // Select generation method if (generationFlags.HasFlag(PasswordGenerationFlags.EquallyDistributed)) { int fullCharSetLength = charSets.Sum(charSet => charSet.Length); // collect characters from the different char sets var charCollector = new StringBuilder(length); for (int i = 0; i < charSets.Length; i++) { string charSet = charSets[i]; int charsFromThisSet = i == charSets.Length - 1 ? length - charCollector.Length : (int)Math.Round((double)charSet.Length / fullCharSetLength * length); for (int j = 0; j < charsFromThisSet; j++) { charCollector.Append(GetRandomCharFromCharSet(charSet, randomBytes[charCollector.Length])); } } // shuffle characters return(ShuffleCharacters(charCollector.ToString(), randomNumberGenerator)); } else { // Combine available characters to one single character set string fullCharSet = ShuffleCharacters(string.Join(string.Empty, charSets), randomNumberGenerator); // Build random password return(new string(randomBytes.Select(number => GetRandomCharFromCharSet(fullCharSet, number)).ToArray())); } } }
public void GeneratedPasswordsAreDifferent(PasswordGenerationFlags flags) { string lastPassword = null; for (int i = 0; i < 100; i++) { string password = Secrets.GeneratePassword(100, flags); Assert.Equal(100, password.Length); if (lastPassword == password) { throw new XunitException("Duplicate detected."); } lastPassword = password; } }
private static IEnumerable <string> BuildCharacterSet(PasswordGenerationFlags generationFlags) { bool readable = generationFlags.HasFlag(PasswordGenerationFlags.ReadableCharSet); if (generationFlags.HasFlag(PasswordGenerationFlags.WithNumbers)) { yield return(readable ? "23456789" : "1234567890"); } if (generationFlags.HasFlag(PasswordGenerationFlags.WithUpperCaseChars)) { yield return(readable ? "ABCDEFGHKLMNPQRSTUVWXYZ" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); } if (generationFlags.HasFlag(PasswordGenerationFlags.WithLowerCaseChars)) { yield return(readable ? "abcdefghkmnpqrstuvwxyz" : "abcdefghijklmnopqrstuvwxyz"); } }