dark mode light mode Search Menu
Search

Create a Random Password Generator with C#

Christiaan Colen on Flickr

This version of the article includes the full code, as well as a link to see this code in action where you can tweak the code to see what happens. This code generates random strong passwords that meet the OWASP recommendations.

If you don’t know C#, or don’t program, don’t worry. This article also is a way to learn how to read code which is an important skill for programmers to learn. To read code, you first have to understand any rules and requirements then look into how code is structured to meet those rules and requirements.

When you read code, ask yourself at least two questions as you read:

  • What is the code supposed to do?
  • How does the code do what it’s supposed to do?

The code that follows uses bold to help you map methods to their use elsewhere in the code.

Password Rules and Requirements

Passwords we create must meet at least 3 of these complexity rules with at least…

  • 1 uppercase character (A-Z)
  • 1 lowercase character (a-z)
  • 1 digit (0-9)
  • 1 special character (punctuation) — do not forget to treat space as special characters too

In addition, no more than…

  • 10 characters (I’ve set it at 8 to satisfy some old password systems, but you can change this)
  • 128 characters
  • 2 identical characters in a row (so 11 is okay but 111 is not allowed)

Create our Program Method

To meet our password rules and requirements, we will create a single method called Program which will call other methods to build and test our password.

There also will be a list of characters to randomly choose from to create our password.

We will add options for which password rules we would like to enforce on each request, so these will be parameters in the Program constructor. The method will need to return the password as a string despite including numbers.

Numbers 1-4 in the code example describe how our Program method works:

  • First, we include any code libraries and system resources we need to use (lines 3-5).
  • Next, our Main() method is called when our software runs (line 9).
  • Within the Main() method, next we call the PasswordGeneratorSettings method (line 18) to create an object and assign its result to a variable called settings. The settings variable is defined by passing a number of password-related variables (lines 11-16) to the PasswordGeneratorSettings method, for example, whether or not to include uppercase or special characters.
  • Finally, our Main() method takes the result of our settings variable and tests that the password we get back from other methods meets our requirements (lines 20-35).
  • While this might sound complicated, look at the code and play a game of find the code block by matching method names in the Program method to other methods in our code, for example, find the PasswordGeneratorSettings method. Think of methods as engines and the values we pass to them as fuel to be processed.

    Also pay attention to the word return. In most programming languages return is how you return the result after a method is done processing data handed to it. If someone hands you paper and a pen and asks you to write the word cat, and you do so, then you return the paper with the word cat written on it. In the Main() code block, data is returned with Console.WriteLine(password) (line 37) which means write the result of the Main() method, your password, to your computer screen or console.

    Define and Create our Password

    If the Program method is our main code block and runs when our software application runs, the other methods in our code are used to define, create, and test our shiny new password.

    PasswordGeneratorSettings (line 41) creates all the settings for our password — whether or not to include uppercase characters, for example — then tests the length of our password is valid and generates an error message if our password is too short or too long.

    PasswordGenerator (line 103) creates the password based on the rules set in the Program method, for example, the maximum number of identical characters allowed. YY is okay but YYY is not.

    The PasswordIsValid method (line 143) checks the password created by the PasswordGenerator method meets our requirements.

    Here is our code to read. To help you read along, key lines are in bold. You also might want to print out this page then use a pen or pencil to mark up the code.

    //Written by Paul Seal. Licensed under MIT. Free for private and commercial uses.
    
    using System;
    using System.Text.RegularExpressions;
    using System.Text;
                        
    public class Program
    {
        public static void Main()
        {
    	const int MAXIMUM_PASSWORD_ATTEMPTS = 10000;
            bool includeLowercase = true;
            bool includeUppercase = true;
            bool includeNumeric = true;
    		bool includeSpecial = false;
            int lengthOfPassword  = 16;
            
    		PasswordGeneratorSettings settings = new PasswordGeneratorSettings(includeLowercase, includeUppercase, includeNumeric, includeSpecial, lengthOfPassword);
    		string password;
    		if (!settings.IsValidLength())
    		{
    			password = settings.LengthErrorMessage();
    		}
    		else
    		{
    			int passwordAttempts = 0;
    			do
    			{
    				password = PasswordGenerator.GeneratePassword(settings);
    				passwordAttempts++;
    			}
    			while (passwordAttempts < MAXIMUM_PASSWORD_ATTEMPTS && !PasswordGenerator.PasswordIsValid(settings, password));
    			
    			password = PasswordGenerator.PasswordIsValid(settings, password) ? password : "Try again";
    		}
            
            Console.WriteLine(password);
        }
    }
    
    public class PasswordGeneratorSettings
    {
    	const string LOWERCASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
    	const string UPPERCASE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    	const string NUMERIC_CHARACTERS = "0123456789";
    	const string SPECIAL_CHARACTERS = @"!#$%&*@\";
    	const int PASSWORD_LENGTH_MIN = 8;
    	const int PASSWORD_LENGTH_MAX = 128;
    	
    	public bool IncludeLowercase {get; set; }
    	public bool IncludeUppercase { get; set; }
    	public bool IncludeNumbers { get; set; }
    	public bool IncludeSpecial { get; set; }
    	public int PasswordLength { get; set; }
    	public string CharacterSet { get; set; }
    	public int MaximumAttempts { get; set; }
    	
    	public PasswordGeneratorSettings(bool includeLowercase, bool includeUppercase, bool includeNumbers, bool includeSpecial, int passwordLength)
    	{
    		IncludeLowercase = includeLowercase;
    		IncludeUppercase = includeUppercase;
    		IncludeNumbers = includeNumbers;
    		IncludeSpecial = includeSpecial;
    		PasswordLength = passwordLength;
    		
    		StringBuilder characterSet = new StringBuilder();
    		
    		if (includeLowercase)
    		{
    			characterSet.Append(LOWERCASE_CHARACTERS);
    		}
    		
    		if (includeUppercase)
    		{
    			characterSet.Append(UPPERCASE_CHARACTERS);
    		}
    		
    		if (includeNumbers)
    		{
    			characterSet.Append(NUMERIC_CHARACTERS);
    		}
    		
    		if (includeSpecial)
    		{
    			characterSet.Append(SPECIAL_CHARACTERS);
    		}
    		
    		CharacterSet = characterSet.ToString();
    	}
    	
    	public bool IsValidLength()
    	{
    		return PasswordLength >= PASSWORD_LENGTH_MIN && PasswordLength <= PASSWORD_LENGTH_MAX;
    	}
    	
    	public string LengthErrorMessage()
    	{
    		return string.Format("Password length must be between {0} and {1} characters", PASSWORD_LENGTH_MIN, PASSWORD_LENGTH_MAX);
    	}
    }
    
    
    public static class PasswordGenerator
    {
    	
    	/// <summary>
    	/// Generates a random password based on the rules passed in the settings parameter
    	/// </summary>
    	/// <param name="settings">Password generator settings object</param>
    	/// <returns>Password or try again</returns>
    	public static string GeneratePassword(PasswordGeneratorSettings settings)
    	{
    		const int MAXIMUM_IDENTICAL_CONSECUTIVE_CHARS = 2;
    		char[] password = new char[settings.PasswordLength];
    		int characterSetLength = settings.CharacterSet.Length;
    		
    		System.Random random = new System.Random();
    		for (int characterPosition = 0; characterPosition < settings.PasswordLength; characterPosition++)
    		{
    			password[characterPosition] = settings.CharacterSet[random.Next(characterSetLength - 1)];
    			
    			bool moreThanTwoIdenticalInARow =
    				characterPosition > MAXIMUM_IDENTICAL_CONSECUTIVE_CHARS
    				&& password[characterPosition] == password[characterPosition - 1]
    				&& password[characterPosition - 1] == password[characterPosition - 2];
    			
    			if (moreThanTwoIdenticalInARow)
    			{
    				characterPosition--;
    			}
    		}
    		
    		return string.Join(null, password);
    	}
    	
    	
    	/// <summary>
    	/// When you give it a password and some settings, it validates the password against the settings.
    	/// </summary>
    	/// <param name="settings">Password settings</param>
    	/// <param name="password">Password to test</param>
    	/// <returns>True or False to say if the password is valid or not</returns>
    	public static bool PasswordIsValid(PasswordGeneratorSettings settings, string password)
    	{
    		const string REGEX_LOWERCASE = @"[a-z]";
    		const string REGEX_UPPERCASE = @"[A-Z]";
    		const string REGEX_NUMERIC = @"[\d]";
    		const string REGEX_SPECIAL = @"([!#$%&*@\\])+";
    		
    		bool lowerCaseIsValid = !settings.IncludeLowercase || (settings.IncludeLowercase && Regex.IsMatch(password, REGEX_LOWERCASE));
    		bool upperCaseIsValid = !settings.IncludeUppercase || (settings.IncludeUppercase && Regex.IsMatch(password, REGEX_UPPERCASE));
    		bool numericIsValid = !settings.IncludeNumbers || (settings.IncludeNumbers && Regex.IsMatch(password, REGEX_NUMERIC));
    		bool symbolsAreValid = !settings.IncludeSpecial || (settings.IncludeSpecial && Regex.IsMatch(password, REGEX_SPECIAL));
    		
    		return lowerCaseIsValid && upperCaseIsValid && numericIsValid && symbolsAreValid;
    	}
    }
    

    Test Our Code

    Be sure to visit the online article if you want to see the code in action. You can change settings in the Program method, for example, and see how it changes the password generated by this code.

    Learn More

    Code on DotNetFiddle

    Press the Run button at the top of the page and random password appears at bottom pane below the code.
    https://dotnetfiddle.net/Y9VnaX

    How to Create a Random Password Generator in C#

    http://www.codeshare.co.uk/blog/how-to-create-a-random-password-generator-in-c/
    https://www.youtube.com/watch?v=TZ-ppATOCYI
    https://github.com/prjseal/PasswordGenerator
    https://www.nuget.org/packages/PasswordGenerator
    https://dotnetfiddle.net/U8TLQA

    The Open Web Application Security Project (OWASP)

    https://www.owasp.org
    https://www.owasp.org/index.php/Authentication_Cheat_Sheet