From 72edccec267776f9c07bd1e79813f4e3d4a3ed10 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Roland=20H=C3=A4der?= Date: Fri, 2 Sep 2016 15:31:18 +0200 Subject: [PATCH] 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 --- .../jusercore/model/user/UserUtils.java | 126 +++++++++++++++++- 1 file changed, 122 insertions(+), 4 deletions(-) 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 + *

+ * @param password Clear text password + *

+ * @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 *

@@ -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 + *

+ * @param password Clear-text password + *

+ * @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 *

@@ -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? -- 2.39.5