Click here to Skip to main content
15,887,027 members
Articles / Desktop Programming / Windows Forms

Password Strength Control

Rate me:
Please Sign up or sign in to vote.
4.76/5 (26 votes)
21 Feb 2010CPOL4 min read 104.4K   6.6K   103   22
Determine the strength of a user entered password.

ChangePassword.png

Introduction

I am currently working on a number of small applications for personal use, all of which require a password to keep the data and application more secure. To ensure that I can only enter a strong password, I decided to create a password strength control which would display how strong the password is - like you get when signing up with lots of websites - where they say Weak, Good, Strong, or Very Strong. To this end, I looked on the Internet for any code, and I could not find much. I did find this website: http://www.passwordmeter.com/[^]. This website seems to me to have a good way of checking password strength, not just checking length or upper and lower case letters. This website also allows you to download the source for this, but it is in JavaScript, and I am writing a C# application, so I decided to use this method of checking the password strength and write my own implementation.

Below is a screenshot of the demo application I used to test the code. The actual PasswordStrengthControl is the brightly coloured box containing the word 'Good'. The table below contains the details of how the password is scored.

PasswordMeter

The Code

The code is split into a class to check the password (PasswordStrength.cs) and a UserControl class (PasswordStrengthControl.cs). There is nothing special about the code. The PasswordStrength class determines the password strength and allows the caller to get the strength as a value (0 to 100), a textual description (Very Weak, Weak, Good, Strong, Very Strong), and a DataTable containing the details of the reason for the score.

The scoring is split into two sections - Additions and Deductions.

Additions

In the additions section of the code, we add to the overall score for things which make the password 'good'. In my code, we check the following:

  • Score += (Password Length *4)
  • Score += ((Password Length - Number of Upper Case Letters)*2)
  • Score += ((Password Length - Number of Lower Case Letters)*2)
  • Score += (Number of Digits * 4)
  • Score += (Number of Symbols * 6)
  • Score += (Number of Digits or Symbols in the Middle of the Password) * 2
  • If (Number of Requirements Met > 3) then Score += (Number of Requirements Met * 2)

Requirements are:

  1. Password Length >= 8
  2. Contains Uppercase Letters (A-Z)
  3. Contains Lowercase Letters (a-z)
  4. Contains Digits (0-9)
  5. Contains Symbols (Char.IsSymbol(ch) or Char.IsPunctuation(ch))

Deductions

In the deductions section of the code, we subtract from the overall score for things which make the password 'weak'. In my code, we check the following:

  • IF Password is all letters THEN Score -= (Password length)
  • IF Password is all digits THEN Score -= (Password length)
  • IF Password has repeated characters THEN Score -= (Number of repeated characters * (Number of repeated characters -1)
  • IF Password has consecutive uppercase letters THEN Score -= (Number of consecutive uppercase characters * 2)
  • IF Password has consecutive lowercase letters THEN Score -= (Number of consecutive lowercase characters * 2)
  • IF Password has consecutive digits THEN Score -= (Number of consecutive digits * 2)
  • IF Password has sequential letters THEN Score -= (Number of sequential letters * 3) E.g.: ABCD or DCBA.
  • IF Password has sequential digits THEN Score -= (Number of sequential digits * 3) E.g.: 1234 or 4321.

Using the Code

Using the code could not be simpler. Add the PasswordStrength.cs file to your project, and then add the namespace to your using section. Then use the code below. All it does is to create a new object of type PasswordStrength, and then you set the password, and read back the score and other details as needed.

C#
PasswordStrength pwdStrength = new PasswordStrength();
pwdStrength.SetPassword("PasswordUnderTest");
int score = pwdStrength.GetScore();
string ScoreDescription = pwdStrength.GetPasswordStrength();
DataTable dtScoreDetails=pwdStrength.GetStrengthDetails();

To use the user control, add the PasswordStrength.cs and PasswordStrengthControl.cs files to your project. Add the namespace to your using section, and build the code. Then, drag and drop the PasswordStrength control onto your Windows Form. In the code, you can call the SetPassword(string Password) method of the control. The control will update itself accordingly.

That is all there is to the code. It is not complex, but solves a small problem. You can use the code as you like, but please let me know if you do use the code.

History

  • 16th February, 2010: Initial post.
  • 20th February, 2010: Article text updated.

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 Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Praise+5 Pin
Joezer BH3-Jun-18 3:31
professionalJoezer BH3-Jun-18 3:31 
PraiseThanks for providing this! Pin
GeoDosch18-Nov-17 3:56
GeoDosch18-Nov-17 3:56 
Questiongreat! Pin
anders k9-May-16 6:31
anders k9-May-16 6:31 
GeneralMy vote of 5 Pin
JayantaChatterjee19-Apr-15 23:36
professionalJayantaChatterjee19-Apr-15 23:36 
GeneralMy vote of 5 Pin
Member 104784065-Dec-14 7:16
Member 104784065-Dec-14 7:16 
GeneralRe: My vote of 5 Pin
Peter Tewkesbury6-Dec-14 4:32
professionalPeter Tewkesbury6-Dec-14 4:32 
GeneralMy vote of 5 Pin
Hüseyin Sekmenoğlu18-Jan-13 22:40
Hüseyin Sekmenoğlu18-Jan-13 22:40 
GeneralRe: My vote of 5 Pin
Peter Tewkesbury6-Dec-14 4:32
professionalPeter Tewkesbury6-Dec-14 4:32 
QuestionCannot open this project in VS 2008 or VS 2010. Is a newer version available? Pin
jroughgarden12-Sep-12 12:37
jroughgarden12-Sep-12 12:37 
AnswerRe: Cannot open this project in VS 2008 or VS 2010. Is a newer version available? Pin
Peter Tewkesbury12-Sep-12 21:46
professionalPeter Tewkesbury12-Sep-12 21:46 
GeneralObectivated version Pin
KNH Prod30-Jul-10 18:34
KNH Prod30-Jul-10 18:34 
Hi Peter and Thanks for your work !
It is always a pleasure to find a good code to start from. Sometimes modifications take longer that created from scratch, but the existing code is a kind of "seed", helping to write sometime something different. Free code on the Web is a good practice and I will not judge your code, it's a gift, no more no less.
But I want to share with you my enhancements.
I suppressed the "old fashioned" DataTable to use a collection of data (a special class storing analysis for each level). I suppressed the Level data (useless), the breaking description (a linq query can help to separate additions/deductions), I modified a bit some rules, refactor a bit too.
So my version is yours, written in a different style. I also separated bonus from malus, making the whole thing clearer.
Well, here is my version, hoping you'll like it :
(note that for security reasons this site is changing some symbols like "greather than", you'll have to reverb back to original symbols in order to compile)

/// <summary>
    /// Determines how strong a password is based on lots of different criteria. 0 is very weak. 100 is Very strong.
    /// The password to check is passed as a property, and other properties can then be used to get the score, the
    /// password strength (as a comment). 
    /// Some other properties allow to get score detail (bonus, malus) and one other can return a full analysis
    /// of the password explaining why it is scoring as it does.
    /// From http://www.codeproject.com/KB/miscctrl/PasswordStrengthControl.aspx heavily modified.
    /// This version : 7/2010 Oliver Dahan http://www.e-naxos.com/blog 
    /// </summary>
    public class PasswordChecker
    {

        private static readonly PasswordChecker current = new PasswordChecker();

        private PasswordChecker()
        { }

        public static PasswordChecker Current
        { get { return current; } }

        private ObservableCollection<AnalysisItem> dtDetails;
        private string password = string.Empty;


        /// <summary>
        /// Gets or sets the password to check.
        /// </summary>
        /// <value>The password.</value>
        public string Password
        {
            get { return password; }
            set
            {
                if (password == value) return;
                password = value;
                CheckPasswordWithDetails(password);
            }
        }

        /// <summary>
        /// Gets the password score.
        /// 0 = weak, 100 = very strong
        /// </summary>
        /// <value>The password score.</value>
        public int PasswordScore
        {
            get
            {
                return dtDetails != null ? dtDetails[0].Total : 0;
            }
        }

        /// <summary>
        /// Gets the password bonus.
        /// </summary>
        /// <value>The password bonus.</value>
        public int PasswordBonus
        {
            get
            {
                return dtDetails != null ? dtDetails[0].Bonus : 0;
            }
        }

        /// <summary>
        /// Gets the password malus.
        /// </summary>
        /// <value>The password malus.</value>
        public int PasswordMalus
        {
            get
            {
                return dtDetails != null ? dtDetails[0].Malus : 0;
            }
        }


        /// <summary>
        /// Returns a textual description of the stregth of the password
        /// </summary>
        /// <returns></returns>
        public string PasswordStrength
        {
            get
            {
                return dtDetails != null ? dtDetails[0].Rate : "Unknown";
            }
        }

        /// <summary>
        /// Returns the details for the password score - Allows you to see why a password has the score it does.
        /// </summary>
        /// <returns></returns>
        public ObservableCollection<AnalysisItem> StrengthDetails
        {
            get { return dtDetails; }
        }

        /// <summary>
        /// This is the method which checks the password and determines the score.
        /// </summary>
        /// <param name="pwd"></param>
        private void CheckPasswordWithDetails(string pwd)
        {
            // Init Vars
            var nMalus = 0;
            var sComplexity = "";
            var iUpperCase = 0;
            var iLowerCase = 0;
            var iDigit = 0;
            var iSymbol = 0;
            var iRepeated = 1;
            var htRepeated = new Dictionary<char, int>();
            var iMiddle = 0;
            var iMiddleEx = 1;
            var consecutiveMode = 0;
            var iConsecutiveUpper = 0;
            var iConsecutiveLower = 0;
            var iConsecutiveDigit = 0;
            const string sAlphas = "abcdefghijklmnopqrstuvwxyz";
            const string sNumerics = "01234567890";
            var nSeqAlpha = 0;
            var nSeqNumber = 0;

            // Create data table to store results
            CreateDetailsTable();
            var drScore = dtDetails[0];

            // Scan password
            foreach (var ch in pwd.ToCharArray())
            {
                // Count digits
                if (Char.IsDigit(ch))
                {
                    iDigit++;

                    if (consecutiveMode == 3)
                        iConsecutiveDigit++;
                    consecutiveMode = 3;
                }

                // Count uppercase characters
                if (Char.IsUpper(ch))
                {
                    iUpperCase++;
                    if (consecutiveMode == 1)
                        iConsecutiveUpper++;
                    consecutiveMode = 1;
                }

                // Count lowercase characters
                if (Char.IsLower(ch))
                {
                    iLowerCase++;
                    if (consecutiveMode == 2)
                        iConsecutiveLower++;
                    consecutiveMode = 2;
                }

                // Count symbols
                if (Char.IsSymbol(ch) || Char.IsPunctuation(ch))
                {
                    iSymbol++;
                    consecutiveMode = 0;
                }

                // Count repeated letters 
                if (Char.IsLetter(ch))
                {
                    if (htRepeated.ContainsKey(Char.ToLower(ch))) iRepeated++;
                    else htRepeated.Add(Char.ToLower(ch), 0);

                    if (iMiddleEx > 1)
                        iMiddle = iMiddleEx - 1;
                }

                if (iUpperCase > 0 || iLowerCase > 0)
                {
                    if (Char.IsDigit(ch) || Char.IsSymbol(ch))
                        iMiddleEx++;
                }
            }

            // Check for sequential alpha string patterns (forward and reverse) 
            for (var s = 0; s < 23; s++)
            {
                var sFwd = sAlphas.Substring(s, 3);
                var sRev = strReverse(sFwd);
                if (pwd.ToLower().IndexOf(sFwd) == -1 && pwd.ToLower().IndexOf(sRev) == -1) continue;
                nSeqAlpha++;
            }

            // Check for sequential numeric string patterns (forward and reverse)
            for (var s = 0; s < 8; s++)
            {
                var sFwd = sNumerics.Substring(s, 3);
                var sRev = strReverse(sFwd);
                if (pwd.ToLower().IndexOf(sFwd) == -1 && pwd.ToLower().IndexOf(sRev) == -1) continue;
                nSeqNumber++;
            }

            // Score += 4 * Password Length
            var nScore = 4 * pwd.Length;
            AddDetailsRow("Password Length", "(n*4)", pwd.Length, pwd.Length * 4, 0);

            // if we have uppercase letetrs Score +=(number of uppercase letters *2)
            if (iUpperCase > 0)
            {
                nScore += ((pwd.Length - iUpperCase) * 2);
                AddDetailsRow("Uppercase Letters", "+((len-n)*2)", iUpperCase, ((pwd.Length - iUpperCase) * 2), 0);
            }
            else
                AddDetailsRow("Uppercase Letters", "+((len-n)*2)", iUpperCase, 0, 0);

            // if we have lowercase letetrs Score +=(number of lowercase letters *2)
            if (iLowerCase > 0)
            {
                nScore += ((pwd.Length - iLowerCase) * 2);
                AddDetailsRow("Lowercase Letters", "+((len-n)*2)", iLowerCase, ((pwd.Length - iLowerCase) * 2), 0);
            }
            else
                AddDetailsRow("Lowercase Letters", "+((len-n)*2)", iLowerCase, 0, 0);


            // Score += (Number of digits *4)
            nScore += (iDigit * 4);
            AddDetailsRow("Numbers", "+(n*4)", iDigit, (iDigit * 4), 0);

            // Score += (Number of Symbols * 6)
            nScore += (iSymbol * 6);
            AddDetailsRow("Symbols", "+(n*6)", iSymbol, (iSymbol * 6), 0);

            // Score += (Number of digits or symbols in middle of password *2)
            nScore += (iMiddle * 2);
            AddDetailsRow("Middle Numbers or Symbols", "+(n*2)", iMiddle, (iMiddle * 2), 0);

            //requirments
            var requirments = 0;
            if (pwd.Length >= 8) requirments++;     // Min password length
            if (iUpperCase > 0) requirments++;      // Uppercase letters
            if (iLowerCase > 0) requirments++;      // Lowercase letters
            if (iDigit > 0) requirments++;          // Digits
            if (iSymbol > 0) requirments++;         // Symbols

            if (pwd.Length>=8)
            {
                nScore += pwd.Length;
                AddDetailsRow("Requirement. Length>8","+(n)",pwd.Length,pwd.Length,0);
            }

            if (iUpperCase>0)
            {
                nScore += iUpperCase*2;
                AddDetailsRow("Requirement. Uppercase","+(n)",iUpperCase,iUpperCase,0);
            }

            if (iLowerCase>0)
            {
                nScore += iLowerCase;
                AddDetailsRow("Requirement. Lowercase","+(n)",iLowerCase,iLowerCase,0);
            }

            if (iDigit>0)
            {
                nScore += iDigit;
                AddDetailsRow("Requirement. Digit","+(n)",iDigit,iDigit,0);
            }

            if (iSymbol>0)
            {
                nScore += iSymbol*2;
                AddDetailsRow("Requirement. Symbol","+(2n)",iSymbol,iSymbol*2,0);
            }

            // If we have more than 3 requirments then
            if (requirments > 3)
            {
                nScore += (requirments * 2);
                AddDetailsRow("3 or more requirments", "+(n*2)", requirments, (requirments * 2), 0);
            }
            else
                AddDetailsRow("Less than 3 requirments", "-(5-n)*2", requirments, 0, (5-requirments)*2);

            //
            // Deductions
            //

            // If only letters then score -=  password length
            if (iDigit == 0 && iSymbol == 0)
            {
                nMalus += pwd.Length;
                AddDetailsRow("Letters only", "-n", pwd.Length, 0, pwd.Length);
            }
            else
                AddDetailsRow("Letters only", "-n", 0, 0, 0);

            // If only digits then score -=  password length
            if (iDigit == pwd.Length)
            {
                nMalus += pwd.Length;
                AddDetailsRow("Numbers only", "-n", pwd.Length, 0, pwd.Length);
            }
            else
                AddDetailsRow("Numbers only", "-n", 0, 0, 0);

            // If repeated letters used then score -= (iRepeated * (iRepeated - 1));
            if (iRepeated > 1)
            {
                nMalus += (iRepeated * (iRepeated));
                AddDetailsRow("Repeat Characters (Case Insensitive)", "-(n(n-1))", iRepeated, 0, iRepeated * (iRepeated));
            }

            // If Consecutive uppercase letters then score -= (iConsecutiveUpper * 2);
            nMalus += (iConsecutiveUpper * 2);
            AddDetailsRow("Consecutive Uppercase Letters", "-(n*2)", iConsecutiveUpper, 0, iConsecutiveUpper * 2);

            // If Consecutive lowercase letters then score -= (iConsecutiveUpper * 2);
            nMalus += (iConsecutiveLower * 2);
            AddDetailsRow("Consecutive Lowercase Letters", "-(n*2)", iConsecutiveLower, 0, iConsecutiveLower * 2);

            // If Consecutive digits used then score -= (iConsecutiveDigits* 2);
            nMalus += (iConsecutiveDigit * 2);
            AddDetailsRow("Consecutive Numbers", "-(n*2)", iConsecutiveDigit, 0, iConsecutiveDigit * 2);

            // If password contains sequence of letters then score -= (nSeqAlpha * 3)
            nMalus += (nSeqAlpha * 3);
            AddDetailsRow("Sequential Letters (3+)", "-(n*3)", nSeqAlpha, 0, nSeqAlpha * 3);

            // If password contains sequence of digits then score -= (nSeqNumber * 3)
            nMalus += (nSeqNumber * 3);
            AddDetailsRow("Sequential Numbers (3+)", "-(n*3)", nSeqNumber, 0, nSeqNumber * 3);

            var fScore = nScore - nMalus;
            /* Determine complexity based on overall score */
            if (fScore > 100) { fScore = 100; } else if (fScore < 0) { fScore = 0; }
            if (fScore >= 0 && fScore < 20) { sComplexity = "Very Weak"; }
            else if (fScore >= 20 && fScore < 40) { sComplexity = "Weak"; }
            else if (fScore >= 40 && fScore < 60) { sComplexity = "Good"; }
            else if (fScore >= 60 && fScore < 80) { sComplexity = "Strong"; }
            else if (fScore >= 80 && fScore <= 100) { sComplexity = "Very Strong"; }

            // Store score and complexity in dataset
            drScore.Bonus = nScore;
            drScore.Malus = nMalus;
            drScore.Rate = sComplexity;
        }

        /// <summary>
        /// Create datatable for results
        /// </summary>
        private void CreateDetailsTable()
        {
            dtDetails = new ObservableCollection<AnalysisItem>();
            AddDetailsRow("Score", "", 0, 0, 0);
        }

        /// <summary>
        /// Helper method to add row into DataTable
        /// </summary>
        /// <param name="description"></param>
        /// <param name="rate"></param>
        /// <param name="count"></param>
        /// <param name="bonus"></param>
        /// <param name="malus"></param>
        /// <returns></returns>
        private void AddDetailsRow(string description, string rate, int count, int bonus, int malus)
        {
            var dr = new AnalysisItem
                         {
                             Description = description ?? "",
                             Rate = rate ?? "",
                             Count = count,
                             Bonus = bonus,
                             Malus = malus
                         };
            dtDetails.Add(dr);
            return;
        }

        /// <summary>
        /// Helper string function to reverse string
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static String strReverse(IEnumerable<char> str)
        {
            return str.Aggregate("", (s, t) => t + s);
        }
    }

    /// <summary>
    /// Analysis result for one rule
    /// </summary>
    public class AnalysisItem
    {
        public string Description { get; set; }
        public string Rate { get; set; }
        public int Count { get; set; }
        public int Bonus { get; set; }
        public int Malus { get; set; }
        public int Total
        {
            get { return Bonus - Malus; }
        }
    }

O.Dahan
www.e-naxos.com

GeneralRe: Obectivated version Pin
Peter Tewkesbury30-Jul-10 22:25
professionalPeter Tewkesbury30-Jul-10 22:25 
GeneralRe: Obectivated version Pin
KNH Prod31-Jul-10 7:13
KNH Prod31-Jul-10 7:13 
PraiseRe: Obectivated version Pin
Joezer BH3-Jun-18 3:30
professionalJoezer BH3-Jun-18 3:30 
GeneralMy vote of 2 Pin
cariolihome23-Feb-10 10:23
cariolihome23-Feb-10 10:23 
GeneralRe: My vote of 2 Pin
Peter Tewkesbury30-Jul-10 22:28
professionalPeter Tewkesbury30-Jul-10 22:28 
Generalkeepass has a very good implementation Pin
Huisheng Chen22-Feb-10 4:06
Huisheng Chen22-Feb-10 4:06 
GeneralRe: keepass has a very good implementation Pin
Peter Tewkesbury30-Jul-10 22:35
professionalPeter Tewkesbury30-Jul-10 22:35 
GeneralBuggy Pin
xliqz21-Feb-10 23:02
xliqz21-Feb-10 23:02 
GeneralRe: Buggy Pin
Peter Tewkesbury30-Jul-10 22:34
professionalPeter Tewkesbury30-Jul-10 22:34 
GeneralRe: Buggy Pin
jtitley20-Jun-11 21:51
jtitley20-Jun-11 21:51 
GeneralWow! Pin
Anthony Daly17-Feb-10 12:56
Anthony Daly17-Feb-10 12:56 

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.