0

I have a password criterion as:

Minimum password length is 8 characters. Password must contain characters from at least 3 of the following 4 categories:

  1. English upper case characters (A through Z)

  2. English lower case characters (a through z)

  3. Numerical digits (0 through 9)

  4. Non alpha-numerical characters (i.e. punctuation such as @#$%^+ ( )

I am using

Regex compareRegex = new Regex(@"^(?=.*[a-z]{1})(?=.*[A-Z]{1}).{7,}$");
Regex numRegex = new Regex(@"[a-zA-Z0-9].{7,}" );

if (compareRegex.IsMatch(strPassword))
{
    int count = txtLoginPassword.Text.IndexOfAny(new char[] { '/', '\\', '[', ']', ':', ';', '|', '=', ',', '+', '*', '?', '<', '>', '@', '\"', '!', '#', '$', '%', '^', '&', '(', ')', '_', '-', '~', '`', '.' });
    if (count >= 0)
        return true;
    if (numRegex.IsMatch(strPassword))
        return true;
    else
        return false;
}
else if (numRegex.IsMatch(strPassword))
{
    bool valid = false;
    int count = txtLoginPassword.Text.IndexOfAny(new char[] { '/', '\\', '[', ']', ':', ';', '|', '=', ',', '+', '*', '?', '<', '>', '@', '\"', '!', '#', '$', '%', '^', '&', '(', ')', '_', '-', '~', '`', '.' });
    valid = (count >= 0);
    return valid;
}
else
{
    return false
}

But it is not working. Pls help

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • See my answer to a very similar (if not duplicate) question: [RegEx for Password Validation (ASP)](http://stackoverflow.com/a/7828925/433790) – ridgerunner Nov 07 '14 at 15:53

2 Answers2

5

Personally I'd just split that up into separate checks for each criteria

private static char[] controlCharacters = 
{ 
     '/', '\\', '[', ']', ':', ';', '|', '=', ',', '+', '*', '?', '<', '>', '@', 
     '\"', '!', '#', '$', '%', '^', '&', '(', ')', '_', '-', '~', '`', '.' 
};

public static bool ValidPassword(string password)
{
    if (password.Length < 8)
        return false;
    int sets = 0;
    if (Regex.IsMatch(password, "[A-Z]"))
        sets++;
    if (Regex.IsMatch(password, "[a-z]"))
        sets++;
    if (Regex.IsMatch(password, "[0-9]"))
        sets++;
    if (password.IndexOfAny(controlCharacters)>=0)
        sets++;
    return sets > 2;
} 

Or if you want an optimized version that doesn't use regular expressions you can do this.

public static bool ValidPassword(string password)
{
    if (password.Length < 8)
        return false;
    bool[] sets = new bool[4];
    foreach (char c in password)
    {
        if(c >= 'A' && c <= 'Z')
            sets[0] = true;
        else if (c >= 'a' && c <= 'z')
            sets[1] = true;
        else if (c >= '0' && c <= '9')
            sets[2] = true;
        else if (controlCharacters.Contains(c))
            sets[3] = true;

        if (sets.Where(b=>b).Count() > 2)
            return true;
    }

    return false;
}

This one just loops through the characters and keeps track of each type it sees and once it's seen 3 or more of each type it returns true.

juharr
  • 31,741
  • 4
  • 58
  • 93
  • This is by far the best way to approach this problem. Avoid the temptation to solve it with a single regex; you can't do it, and trying will only blind you to how simple the problem really is. `` However, I have to question the motivation for your "optimized" version. I mean, you're checking that a string meets a password-strength policy; efficiency is not an issue. I know using a Regex object to check that a string contains a digit can seem like overkill, but it really isn't. `` – Alan Moore Nov 07 '14 at 15:50
  • 1
    @AlanMoore I was bored, what can I say. – juharr Nov 07 '14 at 15:55
0

The regex I use is as follows:

^(?=[^\d_].*?\d)\w(\w|[!@#$%]){8,20} This allows an 8 to 20 character password, which:

  • must contain aplhanumeric characters and select special characters ([!@#$%]).
  • The password also can not start with a digit, underscore or special character
  • Finally, must contain at least one digit.

However for your purposes I might be best tempted to not use a regex, and have the code handle it: analyse the password string and set an int value for each time it finds a lowecase character, uppercase character, number or character from a defined set: with 0 if none are found, and 1 if any are found, that way you just add the totals and if the total is >2 you can allow the password.

In c# you could have functions to test as per the following:

using System;
using System.Collections.Generic;
class Program 
{

    static IEnumerable<string> GetPermutations(string value) 
    {
        if (value.Length == 1) 
        {
            yield return value;
        } 
        else 
        {
            for (int i = 0; i < value.Length; ++i) 
            {
                string a = value[i].ToString();
                foreach (string b in GetPermutations(value.Remove(i, 1))) 
                {
                    yield return a + b;
                }
            }
        }
    }

    static int findCharsInString(string chars, string stringIn)
    {
        foreach (string to_find in GetPermutations(chars)) 
        {
            int i = stringIn.IndexOf(to_find);
            if (i != -1)
            {
                return 1;
            }
            return 0;
        }
    }


    static void Main(string[] args) 
    {
        string lowerCase= "abcdefghijklmnopqrstuvwxyz";
        string upperCase= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        string numChars = "0123456789";
        string allowedSpecials = "@#$%^+()[]";

        string password = "MyNotAllowedPassword";
        string validPW  = "1ValidPassword!";

        if ((findCharsInString(lowerCase, password) + findCharsInString(lowerCase, password) + findCharsInString(lowerCase, password)  + findCharsInString(lowerCase, password)) <3)
        {
            //do something to say its an INVALID password
            Console.WriteLine("INVALID pw!!");
        }
        else
        {
            //do something to say its a VALID password
            Console.WriteLine("Valid pw!!");
        }

        if ((findCharsInString(lowerCase, validPW) + findCharsInString(lowerCase, validPW) + findCharsInString(lowerCase, validPW)  + findCharsInString(lowerCase, validPW)) <3)
        {
            //do something to say its an INVALID password
            Console.WriteLine("INVALID pw!!");
        }
        else
        {
            //do something to say its a VALID password
            Console.WriteLine("Valid pw!!");
        }
    }
}

This gives you the capacity to check each set individually and allows for more control. I have left the code as basic as possible so you can add functions in place of repetition or acertain different ways to modify it to suit your needs more easily. Let me know if anything needs clearing up or you want more info:)

GMasucci
  • 2,834
  • 22
  • 42
  • First why not have the character sets as `char[]` instead of a string so you can use `string.IndexOfAny` or `IEnuemrable.Contains`? Second why do you need to do permutations of the character sets when you only need to check if one character of the set is in the string? – juharr Nov 07 '14 at 13:00