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

Cryptographically Strong Random Password Generation

Rate me:
Please Sign up or sign in to vote.
3.20/5 (15 votes)
1 Nov 2005CPOL3 min read 110.2K   1.8K   55   23
An article on creating a simple, but robust cryptographically strong password generator.

Sample Image - CryptoPasswordGenerator.gif

Introduction

This article is another simple but robust password generator based on similar concepts presented in Kevin Stewart's C# Password Generator.

Background

My purpose for creating this example stemmed from evaluating various third party password generation tools and wanting to know what it would really take to implement my own password generator. In my research I came across Kevin Stewart's article and noticed his use of the RNGCryptoServiceProvider and became curious why this class from the Cryptography namespace was used instead of the Random class.

Further research on the RNGCryptoServiceProvider class led me to find it makes use of Windows modules that have completed FIPS-140 (Federal Information Processing Standard) US government standard which provide a benchmark for implementing cryptographic software. See the Microsoft FIPS - 140 Evaluation article for more information.

Requirements

The types of characters included in the passwords needed to be configurable. The length of the password needed to have a variant length that was chosen randomly. Finally I wanted to randomly determine which character type to append (number, uppercase, lowercase or symbol).

Implementation

Below I've outlined the purpose for each of the methods included in the process of creating the passwords.

RandomNumber.Next(int max)

This method provides a wrapper around the RNGCryptoServiceProvider class allowing for easy creation of a random number. See below...

C#
public static int Next(int max)
{
    if(max <= 0)
    {
        throw new ArgumentOutOfRangeException("max");
    }
    _Random.GetBytes(bytes);
    int value = BitConverter.ToInt32(bytes, 0) % max;
    if(value < 0)
    {
        value = -value;
    }
    return value;
}

The _Random field being used by this method is a static instance of the RNGCryptoServiceProvider class.

Character Types

The char arrays below define the character types that are available for use during password generation.

C#
private static readonly char[] _Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
private static readonly char[] _Numbers = "1234567890".ToCharArray();
private static readonly char[] _Symbols = "!@#$%^&*.?".ToCharArray();

Create()

The Create method does four different functions. It initializes the available character types, determines the length of the password to create, creates and finally returns the generated password.

C#
public string Create()
{
    _CharacterTypes = getCharacterTypes();
    StringBuilder password = new StringBuilder(_MaximumLength);

    //Get a random length for the password.
    int currentPasswordLength = RandomNumber.Next(_MaximumLength);
    //Only allow for passwords greater than or equal to the minimum length.
    if(currentPasswordLength < _MinimumLength)
    { 
        currentPasswordLength = _MinimumLength;
    }
    //Generate the password
    for(int i = 0; i < currentPasswordLength; i++)
    {
        password.Append(getCharacter());
    }
    return password.ToString();
}

getCharacterTypes()

I used an enum to to indicate which character types were available to create the passwords. In this method I iterate through the character types and determine if a particular character type is available for use based on a value set to a corresponding property that indicates whether or not to include a given character type.

C#
private string[] getCharacterTypes()
{
    ArrayList characterTypes = new ArrayList();
    foreach(string characterType in Enum.GetNames(typeof(CharacterType)))
    {
        CharacterType currentType = 
          (CharacterType)Enum.Parse(typeof(CharacterType), 
          characterType, false);
        bool addType = false;
        switch(currentType)
        {
            case CharacterType.Lowercase:
                addType = IncludeLower;
                break;
            case CharacterType.Number:
                addType = IncludeNumber;
                break;
            case CharacterType.Special:
                addType = IncludeSpecial;
                break;
            case CharacterType.Uppercase:
                addType = IncludeUpper;
                break;
        }
        if(addType)
        {
            characterTypes.Add(characterType);
        }
    }
    return (string[])characterTypes.ToArray(typeof(string));
}

getCharacter()

This method randomly determines which type of character to get from the character types, then randomly determines which character from that subset and returns the value.

C#
private string getCharacter()
{
    string characterType = 
      _CharacterTypes[RandomNumber.Next(_CharacterTypes.Length)];
    CharacterType typeToGet = 
      (CharacterType)Enum.Parse(typeof(CharacterType), characterType, false);
    switch(typeToGet)
    {
        case CharacterType.Lowercase:
            return _Letters[RandomNumber.Next(_Letters.Length)].ToString().ToLower();
        case CharacterType.Uppercase:
            return _Letters[RandomNumber.Next(_Letters.Length)].ToString().ToUpper();
        case CharacterType.Number:
            return _Numbers[RandomNumber.Next(_Numbers.Length)].ToString();
        case CharacterType.Special:
            return _Symbols[RandomNumber.Next(_Symbols.Length)].ToString();
    }
    return null;
}

Usage

The example is easy to use and very configurable.

Accept the defaults

If you'd just like to accept the default pre-initialized lengths and to include all of the character types, it's as easy as the following two lines of code:

C#
Password password = new Password();
password.Create();

Configure the types of characters to include

There are properties in the Password class allowing you to indicate whether or not you'd like to IncludeUpper (uppercase), IncludeLower (lowercase), IncludeSymbols (!@#$) and IncludeNumbers (12345*). These properties can be set after a password object has already been created or they can be set during the creation of the object via parameters in the constructor.

C#
Password password = new Password(false, false, true, false);
password.Create();

Create fixed length passwords

One recent suggestion by Gabe Wishnie was to make it so you could explicitly set the password length without having the length chosen at random. A quick test showed this functionality was already built in if used in the following manner.

C#
Password password = new Password();
password.MinimumLength = 8;
password.MaximumLength = 8;
password.Create();

Conclusion

The value in this project can be seen as it could be applied to various concepts. The random number method supplied in this article or Kevin Stewart's could also be used to randomly select words from a string array read from a text file. Combine this with a Captcha control and you'll have a decent validation control. Hope all of you find my implementation of use!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
HaraldGalda26-Jul-16 23:02
professionalHaraldGalda26-Jul-16 23:02 
BugIf no character type is included ... Pin
HaraldGalda26-Jul-16 21:50
professionalHaraldGalda26-Jul-16 21:50 
SuggestionHow to enforce that each required character type will be present Pin
HaraldGalda26-Jul-16 21:44
professionalHaraldGalda26-Jul-16 21:44 
SuggestionReturn value of GetCharacterTypes() Pin
HaraldGalda26-Jul-16 21:38
professionalHaraldGalda26-Jul-16 21:38 
Questionlicense/usage terms Pin
cjfrancione11-Apr-14 10:44
cjfrancione11-Apr-14 10:44 
GeneralRandomNumber.Next() method is slightly biased toward the low range Pin
Jordan Rieger6-Jan-09 11:42
Jordan Rieger6-Jan-09 11:42 
GeneralGood article! A small suggestion though... Pin
Freddy Soderlund5-Jan-08 11:06
Freddy Soderlund5-Jan-08 11:06 
I like your article but I wonder if it wouldn't be a good idea to add lowercase letters to the character types as well?

/Freddy
GeneralRe: Good article! A small suggestion though... Pin
Wil Peck6-Jan-08 10:21
Wil Peck6-Jan-08 10:21 
GeneralRe: Good article! A small suggestion though... Pin
HaraldGalda26-Jul-16 21:46
professionalHaraldGalda26-Jul-16 21:46 
Generalstrong password constraints [modified] Pin
Mr Brown Shoes9-Aug-06 15:57
Mr Brown Shoes9-Aug-06 15:57 
GeneralRe: strong password constraints Pin
Wil Peck14-Aug-06 1:00
Wil Peck14-Aug-06 1:00 
GeneralFor those who want &quot;exclusions&quot; support Pin
yrleu5-Dec-05 1:02
yrleu5-Dec-05 1:02 
GeneralRe: For those who want &amp;quot;exclusions&amp;quot; support Pin
Wil Peck6-Dec-05 3:21
Wil Peck6-Dec-05 3:21 
Generalgood idea but... Pin
ncage9-Nov-05 3:26
ncage9-Nov-05 3:26 
GeneralRe: good idea but... Pin
Wil Peck10-Nov-05 11:21
Wil Peck10-Nov-05 11:21 
QuestionWhy not use PasswordDeriveBytes? Pin
ediazc2-Nov-05 11:06
ediazc2-Nov-05 11:06 
AnswerRe: Why not use PasswordDeriveBytes? Pin
Wil Peck2-Nov-05 11:50
Wil Peck2-Nov-05 11:50 
GeneralRe: Why not use PasswordDeriveBytes? Pin
ediazc2-Nov-05 15:00
ediazc2-Nov-05 15:00 
GeneralRe: Why not use PasswordDeriveBytes? Pin
Wil Peck2-Nov-05 15:18
Wil Peck2-Nov-05 15:18 
GeneralInteresting Pin
bigals2-Nov-05 9:45
bigals2-Nov-05 9:45 
GeneralRe: Interesting Pin
Wil Peck2-Nov-05 14:06
Wil Peck2-Nov-05 14:06 
GeneralRe: Interesting Pin
bigals2-Nov-05 15:42
bigals2-Nov-05 15:42 
GeneralRe: Interesting Pin
Wil Peck2-Nov-05 16:47
Wil Peck2-Nov-05 16:47 

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.