Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#
Article

A C# Password Generator

Rate me:
Please Sign up or sign in to vote.
4.58/5 (31 votes)
17 Aug 20033 min read 449K   11.1K   132   88
An article on implementing a simple password generator class in C#

Introduction

This article illustrates how to create a very simple password generator using C#. Password generators are useful in many applications:

  • Registration/membership systems for Web sites
  • Auto-creation of passwords according to a specified rule
  • Securing application-specific data

The PasswordGenerator class is fairly simple. It exposes several properties that control how the password will be generated.

  • Exclusions: Specifies the set of characters to exclude in password generation.
  • Minimum: Specifies the minimum length of the generated password.
  • Maximum: Specifies the maximum length of the generated password.
  • ConsecutiveCharacters: Controls generation of consecutive characters in the generated password.
  • RepeatingCharacters: Controls generation of repeating characters in the generated password.
  • ExcludeSymbols: Excludes symbols from the set of characters used to generate the password

After setting your desired properties, call the Generate() method to create your new password.

C#
namespace WorkingCode.CodeProject.PwdGen
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public class PasswordGenerator
    {
        public PasswordGenerator() 
        {
            this.Minimum               = DefaultMinimum;
            this.Maximum               = DefaultMaximum;
            this.ConsecutiveCharacters = false;
            this.RepeatCharacters      = true;
            this.ExcludeSymbols        = false;
            this.Exclusions            = null;

            rng = new RNGCryptoServiceProvider();
        }		
		
        protected int GetCryptographicRandomNumber(int lBound, int uBound)
        {   
            // Assumes lBound >= 0 && lBound < uBound
            // returns an int >= lBound and < uBound
            uint urndnum;   
            byte[] rndnum = new Byte[4];   
            if (lBound == uBound-1)  
            {
                // test for degenerate case where only lBound can be returned
                return lBound;
            }
                                                              
            uint xcludeRndBase = (uint.MaxValue -
                (uint.MaxValue%(uint)(uBound-lBound)));   
            
            do 
            {      
                rng.GetBytes(rndnum);      
                urndnum = System.BitConverter.ToUInt32(rndnum,0);      
            } while (urndnum >= xcludeRndBase);   
            
            return (int)(urndnum % (uBound-lBound)) + lBound;
        }

        protected char GetRandomCharacter()
        {            
            int upperBound = pwdCharArray.GetUpperBound(0);

            if ( true == this.ExcludeSymbols )
            {
                upperBound = PasswordGenerator.UBoundDigit;
            }

            int randomCharPosition = GetCryptographicRandomNumber(
                pwdCharArray.GetLowerBound(0), upperBound);

            char randomChar = pwdCharArray[randomCharPosition];

            return randomChar;
        }
        
        public string Generate()
        {
            // Pick random length between minimum and maximum   
            int pwdLength = GetCryptographicRandomNumber(this.Minimum,
                this.Maximum);

            StringBuilder pwdBuffer = new StringBuilder();
            pwdBuffer.Capacity = this.Maximum;

            // Generate random characters
            char lastCharacter, nextCharacter;

            // Initial dummy character flag
            lastCharacter = nextCharacter = '\n';

            for ( int i = 0; i < pwdLength; i++ )
            {
                nextCharacter = GetRandomCharacter();

                if ( false == this.ConsecutiveCharacters )
                {
                    while ( lastCharacter == nextCharacter )
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                if ( false == this.RepeatCharacters )
                {
                    string temp = pwdBuffer.ToString();
                    int duplicateIndex = temp.IndexOf(nextCharacter);
                    while ( -1 != duplicateIndex )
                    {
                        nextCharacter = GetRandomCharacter();
                        duplicateIndex = temp.IndexOf(nextCharacter);
                    }
                }

                if ( ( null != this.Exclusions ) )
                {
                    while ( -1 != this.Exclusions.IndexOf(nextCharacter) )
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                pwdBuffer.Append(nextCharacter);
                lastCharacter = nextCharacter;
            }

            if ( null != pwdBuffer )
            {
                return pwdBuffer.ToString();
            }
            else
            {
                return String.Empty;
            }	
        }
            
        public string Exclusions
        {
            get { return this.exclusionSet;  }
            set { this.exclusionSet = value; }
        }

        public int Minimum
        {
            get { return this.minSize; }
            set	
            { 
                this.minSize = value;
                if ( PasswordGenerator.DefaultMinimum > this.minSize )
                {
                    this.minSize = PasswordGenerator.DefaultMinimum;
                }
            }
        }

        public int Maximum
        {
            get { return this.maxSize; }
            set	
            { 
                this.maxSize = value;
                if ( this.minSize >= this.maxSize )
                {
                    this.maxSize = PasswordGenerator.DefaultMaximum;
                }
            }
        }

        public bool ExcludeSymbols
        {
            get { return this.hasSymbols; }
            set	{ this.hasSymbols = value;}
        }

        public bool RepeatCharacters
        {
            get { return this.hasRepeating; }
            set	{ this.hasRepeating = value;}
        }

        public bool ConsecutiveCharacters
        {
            get { return this.hasConsecutive; }
            set	{ this.hasConsecutive = value;}
        }

        private const int DefaultMinimum = 6;
        private const int DefaultMaximum = 10;
        private const int UBoundDigit    = 61;

        private RNGCryptoServiceProvider    rng;
        private int 			minSize;
        private int 			maxSize;
        private bool			hasRepeating;
        private bool			hasConsecutive;
        private bool            hasSymbols;
        private string          exclusionSet;
        private char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFG" +
            "HIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}\\|;:'\",<" + 
            ".>/?".ToCharArray();                                        
    }
}

The previous version of this class was intended for use in a much larger project (I will be publishing articles on various components of that project soon.). Due to many factors, that code was rushed and proved to be inefficient. In fact, I wish I could go back in time and unpublish it! While this version is definitely better, there is still room for improvement. The generation algorithm can still be optimized. Also, it would be interesting to use regular expressions to both define and validate the passwords we wish to generate. I would have done this, but it's been a long time since I wrote a parser. Maybe for the next version...

In previous articles, I have used the NAnt tool as my build solution. Unfortunately, that team has not produced a stable release that integrates NUnit 2.0. I COULD get the source from the CVS tree, but I'm way too lazy for that. Instead, I have decided to go back to Visual Studio .NET as my development environment. I'm also getting used to test-driven development with NUnit 2.0. If you aren't using this tool for unit testing, I highly recommend you give it a try http://www.nunit.org/. Its use of attributes and reflection to specify test suites, test fixtures and tests, and is quite remarkable and easy to use. I've included my unit test fixture with the source code. Also, try the NUnit Addin for Visual Studio .NET; it's very handy for running your tests within the IDE.

The demo project is a simple Windows Forms UI that allows one to configure the password generator's properties. I must say that while VS.NET is fairly complete and powerful, I just don't like the feel of the forms designer. However, it definitely does the job.

Many thanks to Mike Asher and Julian Roberts for their feedback on the first version of the password generator. Julian was kind enough to test the code in an ASP.NET project and confirmed that it performs much better. Also, I reverted to my old C++ bracing style just to make Nish happy...hope you appreciate the sacrifice! :-)

Change Log

Version 1.2

  • Updated for .NET Framework 1.1
  • Removed FirstCharacter and LastCharacter properties; Exclusions works across all characters
  • Replaced Password property with Generate() method
  • Removed PwdMaskFlags; use Exclusions property and/or ExcludeSymbols property
  • Used RNGCryptoServiceProvider instead of Random for random number generation
  • Updated demo application

Perfection (in design) is achieved not when there is nothing more to add, but rather when there is nothing more to take away. - Antoine de Saint-Exupéry

Version 1.1

  • Improved password generation algorithm

Version 1.0

  • Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Kevin is a software development manager for a small consumer-oriented company in SoHo, NY. He is technology and platform agnostic having worked on PCs, Macintosh and various forms of Unix and Linux. His programming knowledge includes several languages, including C/C++, Java and C#. In the rare moments when his head is not buried in the latest tech book purchase from Amazon, Kevin enjoys spending time with his wife Donna and their dog Kirby.

Comments and Discussions

 
GeneralRe: Alternative Pin
Kevin Stewart16-Jun-04 8:30
Kevin Stewart16-Jun-04 8:30 
GeneralRe: Alternative Pin
smallguy16-Jun-04 11:01
smallguy16-Jun-04 11:01 
GeneralRe: Alternative Pin
tomstrummer24-Aug-05 8:05
tomstrummer24-Aug-05 8:05 
GeneralJust a quick validate for those who need it Pin
Dirk Vandenheuvel18-Feb-04 4:20
Dirk Vandenheuvel18-Feb-04 4:20 
GeneralRe: Just a quick validate for those who need it Pin
Kevin Stewart16-Jun-04 8:32
Kevin Stewart16-Jun-04 8:32 
GeneralGetCryptographicRandomNumber Pin
Anonymous8-Jan-04 5:15
Anonymous8-Jan-04 5:15 
GeneralRe: GetCryptographicRandomNumber Pin
Anonymous8-Jan-04 5:46
Anonymous8-Jan-04 5:46 
GeneralNEED URGENT EXPERTISE PLEASE. Pin
solaces25-Aug-03 5:33
solaces25-Aug-03 5:33 
Smile | :) Hello. I am very new in C programing. i have to do a class project and would really love to include a password requester in it. Where, when someone presses a set of keys, another screen will pop out and request for a password to shut the program down. With out the right pass the pop out will close and return to the original page.

A few things you might want to know.
1)The user cannot use the mouse to shut the program down.
2)I was wondering if i could include the if and else commands on the pop out for the password page.

like

if(password is right)
{
Allow shut down.
}
else
{
pop up closes
}

But was thinking, it won't be case sensitive.

So, i really need all of your expertise. Please... I need it badly. PLease help. I might get lost on this new found site. So if possible, you can reply to me via e-mail at goddime@yahoo.ca

Or else, i would try to find this forum again. thanks alot for your kind help.
Oh yah.. since i am so new, i really hope you can include the command from starting to end. Which includes #include <stdio.h> and ends with

system("pause");
return 0
}

Cos i keep getting confused with the codes on this site. Finally, I need the code in C program. Not other compilers. Teacher's rule. I hope you can include lots of details. So i know why and how the code is working, and created. thanks alot.
GeneralSuggestion: ToCharArray() Pin
Arjan Einbu28-Jul-03 22:15
Arjan Einbu28-Jul-03 22:15 
GeneralRe: Suggestion: ToCharArray() Pin
Kevin Stewart29-Jul-03 3:18
Kevin Stewart29-Jul-03 3:18 
GeneralBias in Password Selection Pin
mdgray28-Jul-03 15:17
mdgray28-Jul-03 15:17 
GeneralRe: Bias in Password Selection Pin
mdgray28-Jul-03 15:39
mdgray28-Jul-03 15:39 
GeneralRe: Bias in Password Selection Pin
Jeffrey Sax28-Jul-03 17:50
Jeffrey Sax28-Jul-03 17:50 
GeneralRe: Bias in Password Selection Pin
mdgray28-Jul-03 21:07
mdgray28-Jul-03 21:07 
GeneralRe: Bias in Password Selection Pin
Kevin Stewart29-Jul-03 3:13
Kevin Stewart29-Jul-03 3:13 
GeneralRe: Bias in Password Selection Pin
mdgray29-Jul-03 5:52
mdgray29-Jul-03 5:52 
GeneralRe: Bias in Password Selection Pin
Kevin Stewart29-Jul-03 6:33
Kevin Stewart29-Jul-03 6:33 
QuestionLatest version ? Pin
jfrandol28-Jul-03 5:24
jfrandol28-Jul-03 5:24 
AnswerRe: Latest version ? Pin
Kevin Stewart29-Jul-03 3:09
Kevin Stewart29-Jul-03 3:09 
GeneralCryptographic random numbers Pin
Jeffrey Sax6-Jul-03 21:59
Jeffrey Sax6-Jul-03 21:59 
GeneralRe: Cryptographic random numbers Pin
Kevin Stewart11-Jul-03 10:38
Kevin Stewart11-Jul-03 10:38 
GeneralExclusion Option doesn't run [.NET FR 1.1] Pin
precchia4-Jul-03 3:26
precchia4-Jul-03 3:26 
GeneralRe: Exclusion Option doesn't run [.NET FR 1.1] Pin
Kevin Stewart4-Jul-03 5:35
Kevin Stewart4-Jul-03 5:35 
GeneralRe: Exclusion Option doesn't run [.NET FR 1.1] Pin
josh_heller17-Jul-03 6:52
josh_heller17-Jul-03 6:52 
GeneralA question and another suggestion Pin
Domenic Denicola2-May-03 4:45
Domenic Denicola2-May-03 4:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.