This question is starting to be viral, and a lot of interesting suggestions have appeared.
Yes, manual writing is difficult. Thus, it is easier to use a template. Although the given regular expression may not be the most optimal, it will be easier to maintain and / or modify, and the user will have better control over the result. It is possible that I missed something, so any constructive criticism will be useful.
These links can be interesting: match at least 2 digits 2 letters in any order in a string , Regular Expression Language , Group Capture
I use this pattern (?=(?:.*?({type})){({count})}) based on all the regular expression I saw in SO. The next step is to replace the required template ( number , special character ...) and add the configuration for the length.
I made a small class for regex building PasswordRegexGenerator.cs Example:
string result = new PasswordRegexGenerator ( ) .UpperCase ( 3, -1 ) // ... {3,} .Number ( 2, 4 ) // ... {2,4} .SpecialCharacter ( 2 ) // ... {2} .Total ( 8,-1 ) .Compose ( ); /// <summary> /// Generator for regular expression, validating password requirements. /// </summary> public class PasswordRegexGenerator { private string _elementTemplate = "(?=(?:.*?({type})){({count})})"; private Dictionary<string, string> _elements = new Dictionary<string, string> { { "uppercase", "[AZ]" }, { "lowercase", "[az]" }, { "number", @"\d" }, { "special", @"\W" }, { "alphanumeric", @"\w" } }; private StringBuilder _sb = new StringBuilder ( ); private string Construct ( string what, int min, int max ) { StringBuilder sb = new StringBuilder ( _elementTemplate ); string count = min.ToString ( ); if ( max == -1 ) { count += ","; } else if ( max > 0 ) { count += "," + max.ToString(); } return sb .Replace ( "({type})", what ) .Replace ( "({count})", count ) .ToString ( ); } /// <summary> /// Change the template for the generation of the regex parts /// </summary> /// <param name="newTemplate">the new template</param> /// <returns></returns> public PasswordRegexGenerator ChangeRegexTemplate ( string newTemplate ) { _elementTemplate = newTemplate; return this; } /// <summary> /// Change or update the regex for a certain type ( number, uppercase ... ) /// </summary> /// <param name="name">type of the regex</param> /// <param name="regex">new value for the regex</param> /// <returns></returns> public PasswordRegexGenerator ChangeRegexElements ( string name, string regex ) { if ( _elements.ContainsKey ( name ) ) { _elements[ name ] = regex; } else { _elements.Add ( name, regex ); } return this; } #region construction methods /// <summary> /// Adding number requirement /// </summary> /// <param name="min"></param> /// <param name="max"></param> /// <returns></returns> public PasswordRegexGenerator Number ( int min = 1, int max = 0 ) { _sb.Append ( Construct ( _elements[ "number" ], min, max ) ); return this; } public PasswordRegexGenerator UpperCase ( int min = 1, int max = 0 ) { _sb.Append ( Construct ( _elements[ "uppercase" ], min, max ) ); return this; } public PasswordRegexGenerator LowerCase ( int min = 1, int max = 0 ) { _sb.Append ( Construct ( _elements[ "lowercase" ], min, max ) ); return this; } public PasswordRegexGenerator SpecialCharacter ( int min = 1, int max = 0 ) { _sb.Append ( Construct ( _elements[ "special" ], min, max ) ); return this; } public PasswordRegexGenerator Total ( int min, int max = 0 ) { string count = min.ToString ( ) + ( ( max == 0 ) ? "" : "," + max.ToString ( ) ); _sb.Append ( ".{" + count + "}" ); return this; }