1+ package PasswordEvaluator ;
2+
3+ import java .io .*;
4+ import java .net .URI ;
5+ import java .net .http .HttpClient ;
6+ import java .net .http .HttpRequest ;
7+ import java .net .http .HttpResponse ;
8+ import java .util .Random ;
9+
10+ /**
11+ * A utility class for evaluating the strength of passwords
12+ * and suggesting new secure passwords if needed.
13+ */
14+ public class PasswordStrengthEvaluator {
15+
16+ private static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
17+
18+ /**
19+ * Evaluates the strength of a given password based on multiple criteria:
20+ * - Length between 8 and 30 characters
21+ * - Contains both uppercase and lowercase letters
22+ * - Contains special characters (@, &, !)
23+ * - Is not a common password from a predefined list
24+ *
25+ * @param password the password to evaluate
26+ * @return a string representing the strength level (MAX, MIN, COMMON)
27+ * @throws IOException if there is a problem reading the common passwords file
28+ */
29+ public static String evaluatePassword (String password ) throws IOException {
30+
31+ boolean hasUppercase = password .matches (".*[A-Z].*" );
32+ boolean hasLowercase = password .matches (".*[a-z].*" );
33+ boolean hasSpecialChar = password .contains ("@" ) || password .contains ("&" ) || password .contains ("!" );
34+ int length = password .length ();
35+
36+ boolean isCommon = isCommon (password );
37+
38+ if (!isCommon && length >= 8 && length < 30 && hasUppercase && hasLowercase && hasSpecialChar ) {
39+ return "Password strength level: MAX" ;
40+ }
41+
42+ if (length < 8 || !hasUppercase || !hasLowercase || !hasSpecialChar || length >= 30 ) {
43+ suggetsPassword (12 );
44+ return "Password strength level: MIN" ;
45+ }
46+
47+ if (isCommon ) {
48+ return "Password strength level: COMMON" + suggetsPassword (12 );
49+ }
50+
51+ return "Unable to determine password strength." ;
52+ }
53+
54+
55+ /**
56+ * Checks whether a given password appears in a list of the most common passwords,
57+ * retrieved from a remote public file on GitHub. The comparison is exact, line by line.
58+ *
59+ * @param password the password to check
60+ * @return true if the password is exactly matched in the list, false otherwise
61+ * @throws RuntimeException if there is an error reading the remote password file
62+ */
63+ public static boolean isCommon (String password ) {
64+ String fileUrl = "https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt" ;
65+
66+ try {
67+ HttpClient client = HttpClient .newHttpClient ();
68+ HttpRequest request = HttpRequest .newBuilder ()
69+ .uri (URI .create (fileUrl ))
70+ .build ();
71+
72+ HttpResponse <InputStream > response = client .send (request , HttpResponse .BodyHandlers .ofInputStream ());
73+
74+ try (BufferedReader reader = new BufferedReader (new InputStreamReader (response .body ()))) {
75+ String line ;
76+ while ((line = reader .readLine ()) != null ) {
77+ if (line .equals (password )) {
78+ return true ;
79+ }
80+ }
81+ }
82+
83+ } catch (Exception e ) {
84+ throw new RuntimeException ("Error reading the common passwords file." , e );
85+ }
86+
87+ return false ;
88+ }
89+
90+
91+
92+
93+
94+ /**
95+ * Suggests a secure random password of a specified length,
96+ * containing only uppercase and lowercase letters.
97+ *
98+ * If the requested length is less than 8, a password with 8 characters is suggested
99+ * and a warning message is returned.
100+ *
101+ * @param length the desired length of the suggested password
102+ * @return a suggested password or a warning message if the length is too short
103+ */
104+ private static String suggetsPassword (int length ) {
105+ Random random = new Random ();
106+ StringBuilder password = new StringBuilder ();
107+ StringBuilder provisionallyPassword = new StringBuilder ();
108+
109+ if (length >= 8 ) {
110+ for (int i = 0 ; i < length ; i ++) {
111+ int index = random .nextInt (LETTERS .length ());
112+ password .append (LETTERS .charAt (index ));
113+ }
114+ return password .toString ();
115+ } else {
116+ for (int i = 0 ; i < 8 ; i ++) {
117+ int index = random .nextInt (LETTERS .length ());
118+ provisionallyPassword .append (LETTERS .charAt (index ));
119+ }
120+ return "Length must be between 8 and 30 characters. Suggested password with 8 characters: " + provisionallyPassword .toString ();
121+ }
122+ }
123+ }
0 commit comments