+2  A: 

Try this:

"^(?=(?:\\D*\\d){2})(?=(?:[^a-z]*[a-z]){2})(?=(?:[^A-Z]*[A-Z]){2})(?=(?:[^!@#$%^&*+=]*[!@#$%^&*+=]){2}).{15,}$"

Here non-capturing groups (?:…) are used to group the conditions and repeat them. I’ve also used the complements of each character class for optimization instead of the universal ..

Gumbo
Thanks Gumbo, however this does fail based on the small amount of evaluation code I have. It could be that my evaluation is wrong.
Woot4Moo
I saw that you edited your post Gumbo, and as such I have tried out that latest change. As it stands the string is looking for 4 instead of 2 uppers,lowers,digits,and specials
Woot4Moo
This is the proper solution thanks Gumbo!
Woot4Moo
@Woot4Moo: I’ve just added quotes. ;-)
Gumbo
apologies on missing it the first time then, it has been quite the day...
Woot4Moo
@Gumbo, nice demonstration of Regex skills :)
macek
Side note: I don't know if anyone is supporting IE5.5 anymore, but this would trigger the regex lookahead bug if used in javascript. More details here: http://blog.stevenlevithan.com/archives/regex-lookahead-bug
macek
+2  A: 

If I understand your question correctly, you want at least 15 characters, and to require at least 2 uppercase characters, at least 2 lowercase characters, at least 2 digits, and at least 2 special characters. In that case you could it like this:

^.*(?=.{15,})(?=.*\d.*\d)(?=.*[a-z].*[a-z])(?=.*[A-Z].*[A-Z])(?=.*[!@#$%^&*+=].*[!@#$%^&*+=]).*$

BTW, your original regex had an extra backslash before the \d

Ken Aspeslagh
That requirement is correct, the extra backslash is for escaping it in my test string in Java land.
Woot4Moo
@Woot4Moo: Doesn’t the second backslash come from a string declaration like `"…\\d…"`?
Gumbo
@gumbo yes it does.
Woot4Moo
@Ken I edited my post to show the password I am using, is there any chance you could help me write a test case to prove it works? It seems to be fairly random as to whether it works or not.
Woot4Moo
I'm just using a simple perl script to test the regex. Your password above won't match because it only has 13 characters and the first part of your regex (?=.{15,}) sets a minimum length of 15 characters.
Ken Aspeslagh
@Ken Oh my sweet jeebus I can't believe I just did that to myself.
Woot4Moo
+1  A: 

I'm not sure that one big regex is the right way to go here. It already looks far too complicated and will be very difficult to change in the future.

My suggestion is to structure the code in the following way:

  • check that the string has 2 lower case characters
    • return failure if not found or continue
  • check that the string has 2 upper case characters
    • return failure if not found or continue
  • etc.

This will also allow you to pass out a return code or errors string specifying why the password was not accepted and the code will be much simpler.

ar
That would be the ideal, however I have to use a regex based on the package we are using.
Woot4Moo
What package are you using and why do you need to use it?
ar
I'm writing installers that use regex validation on complex strings. InstallBuilder
Woot4Moo
Is it the BitRock InstallBuilder? If so, then can you not just specify multiple validation rules, eg. /[A-Z].*[A-Z]/, /[a-z].*[a-z]/ and /[0-9].*[0-9]/, etc. etc.
ar
They have a regular expression validator, thanks for the opinion though.
Woot4Moo
+3  A: 

Personally, I think a password policy that forces use of all three character classes is not very helpful. You can get the same degree of randomness by letting people make longer passwords. Users will tend to get frustrated and write passwords down if they have to abide by too many password rules (which make the passwords too difficult to remember). I recommend counting bits of entropy and making sure they're greater than 60 (usually requires a 10-14 character password). Entropy per character would depend roughly on the number of characters, the range of character sets they use, and maybe how often they switch between character sets (I would guess that passwords like HEYthere are more predictable than heYThEre).

Another note: do you plan not to count the symbols to the right of the keyboard (period, comma, angle brackets, etc.)?

If you still have to find groups of two characters, why not just repeat each pattern? For example, make (?=.\d) into (?=.\d.*\d).

For your test cases, if you are worried that it would only check the first criteria, then write a test case that makes sure each of the following passwords fails (because one and only one of the criteria is not met in each case): Just for fun I reversed the order of expectation of each character set, though it probably won't make a difference unless someone removes/forgets the ?= at some future date.

!@#TESTwithoutnumbers
TESTwithoutsymbols123
&*(testwithoutuppercase456
+_^TESTWITHOUTLOWERCASE3498

I should point out that technically none of these passwords should be acceptable because they use dictionary words, which have about 2 bits of entropy per character instead of something more like 6. However, I realize that it's difficult to write a (maintainable and efficient) regular expression to check for dictionary words.

Kimball Robinson
As a software tester, I have to take requirements back to someone and explain why they're insufficient or wrong. Do you have any ability to present an alternate solution or alternate requirements?
Kimball Robinson
No I do not have that ability, nor do i foresee anyone on the team having that ability in the near future for a myriad of reasons.
Woot4Moo