From: Roland Häder <roland@mxchange.org>
Date: Fri, 2 Sep 2016 13:31:18 +0000 (+0200)
Subject: Continued a bit:
X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=72edccec267776f9c07bd1e79813f4e3d4a3ed10;p=juser-core.git

Continued a bit:
- random password generator massively improved:
- splitted alphabet string up in lower-case, upper-case, number and sign (aka. part)
- take random "part" and add char from it to random password
- also added calculation of password entropy value and password strength (0-100)
- this idea is based on:
  http://stackoverflow.com/questions/14591701/how-to-check-password-strength-in-vaadin
  http://stackoverflow.com/questions/1614811/how-do-i-measure-the-strength-of-a-password
---

diff --git a/src/org/mxchange/jusercore/model/user/UserUtils.java b/src/org/mxchange/jusercore/model/user/UserUtils.java
index 0ab7e74..d0f72e2 100644
--- a/src/org/mxchange/jusercore/model/user/UserUtils.java
+++ b/src/org/mxchange/jusercore/model/user/UserUtils.java
@@ -21,6 +21,7 @@ import java.security.SecureRandom;
 import java.text.MessageFormat;
 import java.util.Properties;
 import java.util.Random;
+import java.util.regex.Pattern;
 import org.apache.commons.codec.digest.Crypt;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.mxchange.jcontacts.contact.Contact;
@@ -36,7 +37,28 @@ public class UserUtils implements Serializable {
 	/**
 	 * Password alphabet
 	 */
-	private static final String PASSWORD_ALPHABET = "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY0123456789-/?!_+#@"; //NOI18N
+	private static final String PASSWORD_ALPHABET =
+								UserUtils.PASSWORD_ALPHABET_PARTS[0] +
+								UserUtils.PASSWORD_ALPHABET_PARTS[1] +
+								UserUtils.PASSWORD_ALPHABET_PARTS[2] +
+								UserUtils.PASSWORD_ALPHABET_PARTS[3];
+
+	/**
+	 * Password alphabet parts
+	 */
+	private static final String[] PASSWORD_ALPHABET_PARTS = {
+		// lower-case
+		"abcdefghijklmnopqrstuvwxyz", //NOI18N
+
+		// upper-case
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ", //NOI18N
+
+		// numbers
+		"0123456789", //NOI18N
+
+		// characters
+		"!\"$%&/()=?{[]}@+*#-_,.;:<|>" //NOI18N
+	};
 
 	/**
 	 * Minimum password length
@@ -61,6 +83,29 @@ public class UserUtils implements Serializable {
 		RANDOM_NUMBER_GENERATOR = new SecureRandom();
 	}
 
+	/**
+	 * Calculates entropy value for given password, higher means better. This
+	 * method is based on
+	 * http://stackoverflow.com/questions/14591701/how-to-check-password-strength-in-vaadin
+	 * <p>
+	 * @param password Clear text password
+	 * <p>
+	 * @return Entropy factor
+	 */
+	public static double calculateEntropyFactor (final String password) {
+		// Should not be null
+		if (null == password) {
+			// Throw NPE
+			throw new NullPointerException("password is null"); //NOI18N
+		}
+
+		// Calculate it
+		double entropyFactor = password.length() * Math.log10(PASSWORD_ALPHABET.length()) / Math.log10(2);
+
+		// Return it
+		return entropyFactor;
+	}
+
 	/**
 	 * Creates a pseudo-random password with given length
 	 * <p>
@@ -83,11 +128,14 @@ public class UserUtils implements Serializable {
 
 		// Start creating it
 		for (int i = 0; i < length; i++) {
+			// Take random part
+			String alphabet = PASSWORD_ALPHABET_PARTS[RANDOM_NUMBER_GENERATOR.nextInt(PASSWORD_ALPHABET_PARTS.length)];
+
 			// Generate random number
-			int pos = RANDOM_NUMBER_GENERATOR.nextInt(PASSWORD_ALPHABET.length());
+			int pos = RANDOM_NUMBER_GENERATOR.nextInt(alphabet.length());
 
 			// Get char at this position and add it to the final password
-			password.append(String.valueOf(PASSWORD_ALPHABET.charAt(pos)));
+			password.append(String.valueOf(alphabet.charAt(pos)));
 		}
 
 		// Should have the wanted length
@@ -97,6 +145,73 @@ public class UserUtils implements Serializable {
 		return password.toString();
 	}
 
+	/**
+	 * Determines given password's strength: 0 = bad, 100 = best. This method is
+	 * based on
+	 * http://stackoverflow.com/questions/1614811/how-do-i-measure-the-strength-of-a-password
+	 * <p>
+	 * @param password Clear-text password
+	 * <p>
+	 * @return Strength of password
+	 */
+	public static float determinePasswordStrength (final String password) {
+		// Should not be null
+		if (null == password) {
+			// Throw NPE
+			throw new NullPointerException("password is null"); //NOI18N
+		} else if (password.isEmpty()) {
+			// Is empty
+			return 0.0f;
+		}
+
+		// Init score
+		float score = 0.0f;
+
+		//password length
+		score += password.length() * calculateEntropyFactor(password);
+
+		//password has 3 numbers
+		if (Pattern.matches("/(.*[0-9].*[0-9].*[0-9])/", password)) { //NOI18N
+			score += 5;
+		}
+
+		//password has 2 symbols
+		if (Pattern.matches("/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/", password)) { //NOI18N
+			score += 5;
+		}
+
+		//password has Upper and Lower chars
+		if (Pattern.matches("/([a-z].*[A-Z])|([A-Z].*[a-z])/", password)) { //NOI18N
+			score += 10;
+		}
+
+		//password has number and chars
+		if (Pattern.matches("/([a-zA-Z])/", password) && Pattern.matches("/([0-9])/", password)) { //NOI18N
+			score += 15;
+		}
+
+		//password has number and symbol
+		if (Pattern.matches("/([!,@,#,$,%,^,&,*,?,_,~])/", password) && Pattern.matches("/([0-9])/", password)) { //NOI18N
+			score += 15;
+		}
+
+		//password has char and symbol
+		if (Pattern.matches("/([!,@,#,$,%,^,&,*,?,_,~])/", password) && Pattern.matches("/([a-zA-Z])/", password)) { //NOI18N
+			score += 15;
+		}
+
+		//password is just a nubers or chars
+		if (Pattern.matches("/^[a-zA-Z]+$/", password) || Pattern.matches("/^[0-9]+$/", password)) { //NOI18N
+			score -= 10;
+		}
+
+		// Larger than 100 is not allowed
+		score = Math.max(score, 100.0f);
+
+		// Return it
+		return score;
+	}
+
 	/**
 	 * Hashes given user password and adds a salt to it
 	 * <p>
@@ -149,7 +264,10 @@ public class UserUtils implements Serializable {
 	 * @return Generated key
 	 */
 	public static String generatedConfirmationKey (final User user) {
-		// Generates random string by creating a random, encrypted password
+		/**
+		 * Generates random string by creating a random, encrypted password
+		 * which gives nice entropy to start with.
+		 */
 		StringBuilder key = new StringBuilder(encryptPassword(generateRandomUserName()));
 
 		// Is user set?