Passwords are the most basic information protection devices. But many people compromise the security of their data by choosing passwords that are easy for a human or a bot to guess. The table below summarizes one set of criteria for determining the strength of a password, and shows how to apply these criteria to two different example passwords.
Example 1: "ECOO()123abcd9876" Example 2: "1234512345"
Score | Description | Ex1 | Ex2 |
---|---|---|---|
Length | Score 4 points for each character in the password. | +68 | +40 |
Basic Requirements | To qualify, must be at least 8 chars long and also contain 3 of the four basic character types (upper case letters, lower case letters, digits, and symbols*). Score two points for the length plus another two for each of the four types of characters it contains. | +10 | -- |
Upper Case | Add (length - n) * 2, where n is the number of upper case letters and n > 0. | +26 | -- |
Lower Case | Add (length - n) * 2, where n is the number of lower case letters and n > 0. | +26 | -- |
Digits | Add 4 * n, where n is the number of digits, but only if n < length. | +28 | -- |
Symbols | Add 6 * n, where n is the number of symbol characters. | +12 | -- |
Middle Digits and Symbols | Add 2 * n, where n is the number of digits and symbols not in the first or last position. | +16 | +16 |
Letters Only | If the password contains only letters, subtract 1 point for each letter. | -- | -- |
Digits Only | If the password contains only digits, subtract 1 point for each digit. | -- | -10 |
Consecutive Upper Case | Subtract 2 * (n - 1) for each set of n consecutive upper case characters. | -6 | -- |
Consecutive Lower Case | Subtract 2 * (n - 1) for each set of n consecutive lower case characters. | -6 | -- |
Consecutive Digits | Subtract 2 * (n - 1) for each set of n consecutive digits. | -10 | -18 |
Sequential Letters | Subtract 3 * (n - 2), where n is the length of the longest case-sensitive sequence of letters longer than 2 (e.g. "abcd" or "CBA" but not "aBcD" or "SJD" or "a"). | -6 | -- |
Sequential Digits | Subtract 3 * (n - 2), where n is the length of the longest sequence of digits longer than 2 (e.g. "9876" but not "28" or "6"). | -6 | -9 |
TOTAL SCORE | Add up all the above. Negative scores become 0. Scores over 100 become 100. 0-20 = Very Weak, 21-40 = Weak, 41-60 = Good, 61-80 = Strong, 81-100 = Very Strong | 100 VERY STRONG |
19 VERY WEAK |
DATA21.txt (DATA22.txt for the second try) contains ten passwords, one per line. Write a program that reads each password, computes its total score based on the above criteria, and categorizes the password as Very Weak, Weak, Good, Strong, or Very Strong.
Sample Input ECOO()123abcd9876 1234512345 ecoo2012 |
Sample Output Very Strong (score = 100) Very Weak (score = 19) Good (score = 47) |
import java.io.File; import java.util.ArrayList; import java.util.Scanner; public class Problem_2_Password_Strength { static int score; // to be modified by methods public static void main(String[] args) { try { Scanner file = new Scanner(new File("C:\\Users\\Mike\\Desktop\\problem_2_password_strength_DATA20.txt")); ArrayList<String> list = new ArrayList<String>(); // read in the file to the list while (file.hasNextLine()) { list.add(file.nextLine()); } for (String password : list) { score = 0; // reset it each password score += password.length() * 4; // call methods to alter the score based on the password grading basicRequirements(password); counts(password); if (password.matches("[0-9]+")) // regex; only digits score -= password.length(); // -1 for each digit if (password.matches("[a-zA-Z]+")) // regex; only letters score -= password.length(); // -1 for each letter middles(password); // check consecutive types for each (uppercase letters, lowercase letters and digits) consecutiveTypes(password, "[0-9]"); // or consecutiveTypes(password, "\\d"); consecutiveTypes(password, "[a-z]"); consecutiveTypes(password, "[A-Z]"); sequentialTypes(password); // negative score goes to 0, score > 100 goes to 100 if (score < 0) score = 0; if (score > 100) score = 100; // display output based on intervals if (score >= 0 && score <= 20) System.out.println("Very Weak (score = " + score + ")"); if (score >= 21 && score <= 40) System.out.println("Weak (score = " + score + ")"); if (score >= 41 && score <= 60) System.out.println("Good (score = " + score + ")"); if (score >= 61 && score <= 80) System.out.println("Strong (score = " + score + ")"); if (score >= 81 && score <= 100) System.out.println("Very Strong (score = " + score + ")"); } } catch (Exception e) { e.printStackTrace(); } } public static void middles(String password) { for (int i = 1; i < password.length() - 1; i++) { // for every character in password string String c = password.substring(i, i+1); // current character in password if (c.matches("[^a-zA-Z]")) { // not a letter, so a digit or symbol score += 2; } } } /* parameter s is the password string and regex is the type to check. ex: digits would be "[0-9]" or "\\d" */ public static void consecutiveTypes(String s, String regex) { // ArrayList that holds the length of consecutive set (as element) for each consecutive set (each set its own element) ArrayList<Integer> type = new ArrayList<Integer>(); for (int i = 0; i < s.length()-1; i++) { // for every character in password int count = 1; // length of consecutive set String s1 = s.substring(i,i+1), s2 = s.substring(i+1,i+2); // adjacent characters // while loop to keep iterating through a consecutive set while (s1.matches(regex) && s2.matches(regex)) { // if adjacent are both the same type count++; // length of consecutive set increases i++; // move over one index in the password because we've counted it in the consecutive set // can't check any more adjacent characters because we're at the end of the password, so break if (i > s.length() - 2) { break; } // reset the adjacent characters s1 = s.substring(i,i+1); s2 = s.substring(i+1,i+2); } type.add(count); // add length of consecutive set to list once the while loop is done (consecutive set is over) } // update the score for (int length : type) { // for every number length of consecutive set of every consecutive set score -= 2 * (length - 1); } } public static void sequentialTypes(String s) { // ArrayLists that hold the length of sequential sets (as element) for each sequential set (each set its own element) ArrayList<Integer> letters = new ArrayList<Integer>(), digits = new ArrayList<Integer>(); for (int i = 0; i < s.length()-1; i++) { // for every character in password int count = 1; // length of sequential set char c1 = s.charAt(i), c2 = s.charAt(i+1); // adjacent characters // check if sequence should be increasing or decreasing (difference in ASCII values positive or negative) int upOrDown; if ((int)(c1) - (int)(c2) < 0) upOrDown = -1; else upOrDown = 1; // while loop to keep iterating through a consecutive set // both characters are digits; and the difference between the two ASCII values is 1 (they're sequential) and following the sequential trend while (String.valueOf(c1).matches("[0-9]") && String.valueOf(c2).matches("[0-9]") && (int)(c1) - (int)(c2) == upOrDown) { count++; // length of sequential set increases i++; // move over one index in the password because we've counted it in the sequential set // can't check any more adjacent characters because we're at the end of the password, so break if (i > s.length() - 2) { break; } // reset the adjacent characters c1 = s.charAt(i); c2 = s.charAt(i+1); } digits.add(count); // add length of sequential set to list once the while loop is done (sequential set is over) } // do the same thing as the above for loop (with digit type), but instead with lowercase letters and then again with uppercase letters // but add the sequential lengths to the same list because we are checking sequential LETTERS, so the longest // sequential set length needs to include both lowercase and uppercase for the letters for (int i = 0; i < s.length()-1; i++) { int count = 1; char c1 = s.charAt(i), c2 = s.charAt(i+1); int upOrDown; if ((int)(c1) - (int)(c2) < 0) upOrDown = -1; else upOrDown = 1; while (String.valueOf(c1).matches("[a-z]") && String.valueOf(c2).matches("[a-z]") && (int)(c1) - (int)(c2) == upOrDown) { count++; i++; if (i > s.length() - 2) { break; } c1 = s.charAt(i); c2 = s.charAt(i+1); } letters.add(count); } for (int i = 0; i < s.length()-1; i++) { int count = 1; char c1 = s.charAt(i), c2 = s.charAt(i+1); int upOrDown; if ((int)(c1) - (int)(c2) < 0) upOrDown = -1; else upOrDown = 1; while (String.valueOf(c1).matches("[A-Z]") && String.valueOf(c2).matches("[A-Z]") && (int)(c1) - (int)(c2) == upOrDown) { count++; i++; if (i > s.length() - 2) { break; } c1 = s.charAt(i); c2 = s.charAt(i+1); } letters.add(count); } int max = 2; // initialize to 2 just incase there is no other max found, this way the default is less than what can be updated to the score for (int i : digits) { // for every sequential digit set length if (i > max) { // if this length is greater than the previous max max = i; // this is the new max } } if (max > 2) { // if length is longer than 2 (has to be a sequence of at least 3 in length) score -= 3 * (max - 2); // update score } // do the same thing as above except now with every sequential LETTER set length max = 2; for (int i : letters) { if (i > max) { max = i; } } if (max > 2) { score -= 3 * (max - 2); } } public static void counts(String password) { // counts of each int uppers = 0, lowers = 0, digits = 0, symbols = 0; for (int i = 0; i < password.length(); i++) { // for every character in password String c = password.substring(i,i+1); // current character in password // count occurrences if (c.matches("[a-z]")) { // lowercase lowers++; } else if (c.matches("[A-Z]")) { // uppercase uppers++; } else if (c.matches("[0-9]")) { // digits digits++; } else if (c.matches("[^a-zA-Z0-9]")) { // symbols symbols++; } } if (uppers > 0) { score += (password.length() - uppers) * 2; } if (lowers > 0) { score += (password.length() - lowers) * 2; } if (digits < password.length()) { score += 4 * digits; } score += 6 * symbols; } /* 8 length * 3 of: uppercase, lowercase, symbol, digit * digits: 48-57 */ public static void basicRequirements(String password) { boolean longEnough = false; if (password.length() > 7) longEnough = true; int types = 0; boolean digit = false, uppercase = false, lowercase = false, symbol = false; for (int i = 0; i < password.length(); i++) { // for every character in password string // current character in password string String s = password.substring(i,i+1); char ch = password.charAt(i); if (s.matches("[0-9]")) // regex //if (Character.isDigit(ch)) // Character class //if (ch >= 48 && ch <= 57) // ASCII value digit = true; else if (s.matches("[A-Z]")) // regex //else if (Character.isUpperCase(ch)) // Character class //else if (ch >= 65 && ch <= 90) // ASCII value uppercase = true; // else if (s.matches("[a-z]")) // regex //else if (Character.isLowerCase(ch)) // Character class //else if (ch >= 97 && ch <= 122) // ASCII value lowercase = true; else if (s.matches("[^a-zA-Z0-9]")) // regex //else if (!Character.isLetterOrDigit(ch)) // Character class //else if (ch < 48 || (ch > 57 && ch < 65) || (ch < 97 && ch > 90) || ch > 122) // ASCII value //else symbol = true; } if (symbol) types++; if (digit) types++; if (uppercase) types++; if (lowercase) types++; boolean qualify = false; if (types >= 3 && longEnough) qualify = true; if (qualify) { score += 2 + types * 2; } } }
Using their sample input file:
ECOO()123abcd9876 1234512345 ecoo2012 ecoo Ecoo EcooEcoo123 (TheBest?)45(45)abCD Ecoo123 39283 foo() foobar()
And the output to that is:
Very Strong (score = 100) Very Weak (score = 19) Good (score = 47) Very Weak (score = 6) Very Weak (score = 16) Very Strong (score = 81) Very Strong (score = 100) Good (score = 53) Very Weak (score = 13) Weak (score = 34) Weak (score = 40)DOWNLOAD as .txt
Using their first judging test input:
12345678 a (**) 1abcCBA2 abcdcbAedcba education2012() Education() (An)(ApPlE)(a)(DaY)(kEePs)(ThE)(dOcToR)(aWaY) aBc abcdefg0987654321
And the output to that is:
Very Weak (score = 4) Very Weak (score = 3) Good (score = 44) Good (score = 57) Weak (score = 33) Very Strong (score = 93) Strong (score = 78) Very Strong (score = 100) Very Weak (score = 15) Strong (score = 80)DOWNLOAD as .txt
And that is exactly the expected output.
Using their second judging test input:
RABBITRABBIT !nopasaran! god LifeBeginsAt40 IHaveADream(1963) 0 012 aBcDeFgHiJkLmNo ponmlkj543 AAAAAAAAAAAAAAAAAA
And the output to that is:
Very Weak (score = 14) Good (score = 41) Very Weak (score = 5) Very Strong (score = 92) Very Strong (score = 100) Very Weak (score = 3) Very Weak (score = 4) Strong (score = 75) Weak (score = 28) Very Weak (score = 20)DOWNLOAD as .txt
And that is exactly the expected output.
Created: April 21, 2014
Last updated: June 14, 2014
Completed in full by: Michael Yaworski