One Regex
You can use zero-width positive lookahead assertions (see MSDN's "Regular Expression Language - Quick Reference" - in the Grouping Constructs section) to check for your ANDed password requirements - without having to explicitly cover all the orders in which the required password components may occur:
^(?=.*?[a-zA-Z])(?=.*?[0-9])(?=.*?[!@#$]).{3,}$
This works as follows:
(?=.*?[a-zA-Z])
- an alpha character found ahead
(?=.*?[0-9])
- and a number found ahead
(?=.*?[!@#$])
- and an allowed special character ahead
.{3,}
- three or more characters in total
I have shared a regex fiddle based on this that you might find useful.
Property-Per-Criterion Password Checker
Per @LeffeBrune's comment, you should consider alternatively checking with a dedicated password-checker class that exposes a property per criterion.
For example, here is a quick & dirty PoC in LINQPad 4...
void Main()
{
var passwords = new string[] {"password", "passw0rd", "passw0rd!", "123456", "!#@$"};
foreach (var pw in passwords)
{
var checker = new PasswordChecker(pw);
var isValid = checker.IsValid;
Console.WriteLine("Password {0} is {1}valid.", pw, isValid ? "" : "in");
if (!isValid)
{
Console.WriteLine(" Has alpha? {0}", checker.HasAlpha ? "Yes." : "NO!");
Console.WriteLine(" Has number? {0}", checker.HasNumber ? "Yes." : "NO!");
Console.WriteLine(" Has special? {0}", checker.HasSpecial ? "Yes." : "NO!");
Console.WriteLine(" Has length? {0}", checker.HasLength ? "Yes." : "NO!");
}
}
}
public class PasswordChecker
{
public const int MINIMUM_LENGTH = 3;
private string _password;
public PasswordChecker(string password)
{
_password = password;
}
public bool HasAlpha
{
get
{
return Regex.Match(_password, "(?=.*?[a-zA-Z])").Success;
}
}
public bool HasNumber
{
get
{
return Regex.Match(_password, "(?=.*?[0-9])").Success;
}
}
public bool HasSpecial
{
get
{
return Regex.Match(_password, "(?=.*?[!@#$])").Success;
}
}
public bool HasLength
{
get
{
return _password.Length >= MINIMUM_LENGTH;
}
}
public bool IsValid
{
get
{
return HasLength && HasAlpha && HasNumber && HasSpecial;
}
}
}
...that produces the following output:
Password password is invalid.
Has alpha? Yes.
Has number? NO!
Has special? NO!
Has length? Yes.
Password passw0rd is invalid.
Has alpha? Yes.
Has number? Yes.
Has special? NO!
Has length? Yes.
Password passw0rd! is valid.
Password 123456 is invalid.
Has alpha? NO!
Has number? Yes.
Has special? NO!
Has length? Yes.
Password !#@$ is invalid.
Has alpha? NO!
Has number? NO!
Has special? Yes.
Has length? Yes.
You could take this quick & dirty PoC much further of course, but the benefits of knowing what criterion or criteria failed are hopefully clear.