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