Create a secure random password in Java with minimal special character requirements

How to create a random password that matches the system requirements and character set requirements in Java?

I need to create a random password with a length of 10-14 characters and have at least one uppercase, one lowercase and one special character. Unfortunately, some special characters are too special and cannot be used, so I can not use only printed ASCII.

Many of the examples on this site generate a random password or session key without sufficient entropy in characters or without realistic requirements in business settings like the ones above, so I ask a more pointed question to get a better answer.

My character set, each special character on a standard American keyboard, with the exception of a space:

AZ az 0-9 ~` !@ #$%^&*()-_=+[{]}\|;:'",<.>/? 
+9
java security passwords random
source share
4 answers

I recently learned about Passy . It provides the necessary functionality needed in its PasswordGenerator class . It randomly generates passwords that meet the requirements similar to what is written below, using CharacterRules, not PasswordCharacterSets, as I did below. Instead of storing a list of unused indexes for inserting random characters, it simply shuffles the character buffer after inserting characters that match the requirements.

Below, left earlier, I recommend using Passay, if your licensing allows it, this code should work differently and provides details of why the generated passwords are cryptographically strong

I ended up writing this code twice. Once, to get a random character result, but it turned out that the distribution of characters depends on the size of the character set (oops!). I rewrote it, and now you just have to copy / paste the code and change Main.java to the character sets you want. Although this could have been done differently, I think this is a relatively simple approach to get the right result, and I encourage reuse, commentary, criticism, and thoughtful change.

PasswordGenerator code is managed as follows:

  • Min / Max Length : Set using a random number
  • PasswordCharacterSet : it is assumed that all PasswordCharacterSet passed to PasswordGenerator consist of unique character sets, otherwise random characters will skew towards duplicates.
  • Minimum characters PasswordCharacterSet : The minimum characters used for this character set.

The main bits for the actual password generation:

  • Randomness of chance : we use SecureRandom, which is supported by a cryptographically strong PRNG, and not by the Random class, which is not one.
  • Random character for password : all pw char array indices are added to the remaining indices array. When we call addRandomCharacters, it randomly deletes the index, and we use the remote index to populate the array.
  • Random characters : addRandomCharacters selects a random index from the character index, which we use and add to the pw array.
  • The minimum number of characters of each type is set : first, we simply select the minimum number of characters. We select the minimum number of random values ​​from each character set and then move on.
  • Random distribution for the remaining characters : after the minimum values ​​have been set, we want to make the remaining characters random for all character sets. All characters are added to one array. The remaining slots are filled using the same strategy for previous character sets.

Password complexity description: Password complexity is usually spoken in entropy bits. Here are the number of possibilities for your keyspace:

There is at least one uppercase alphabetic character (out of 26), one lowercase alphabetic character (out of 26), one digit (out of 10) and one special character (out of 32), a way to calculate the number of possibilities. this is the number of possibilities for each character multiplied by the number of characters, since they are arbitrarily placed in a string. So, we know the possibilities for four characters:

 Required Characters = 26*26*10*32=216,320 

All other characters have 94 (26 + 26 + 10 + 32) possibilities each

Our calculation:

 Characters Possibilities Bits of Entropy 10 chars 216,320*94^6 = 149,232,631,038,033,920 ~2^57 11 chars 216,320*94^7 = 14,027,867,317,575,188,480 ~2^63 12 chars 216,320*94^8 = 1,318,619,527,852,067,717,120 ~2^70 13 chars 216,320*94^9 = 123,950,235,618,094,365,409,280 ~2^76 14 chars 216,320*94^10 = 11,651,322,148,100,870,348,472,320 ~2^83 

Remember that if you want to get the most reliable passwords, you should always choose the maximum possible number of characters, which in this case is 14.

Main.java

 package org.redtown.pw; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import org.redtown.pw.PasswordGenerator.PasswordCharacterSet; public class Main { public static void main(String[] args) { Set<PasswordCharacterSet> values = new HashSet<PasswordCharacterSet>(EnumSet.allOf(SummerCharacterSets.class)); PasswordGenerator pwGenerator = new PasswordGenerator(values, 10, 14); for(int i=0; i < 10; ++i) { System.out.println(pwGenerator.generatePassword()); } } private static final char[] ALPHA_UPPER_CHARACTERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; private static final char[] ALPHA_LOWER_CHARACTERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; private static final char[] NUMERIC_CHARACTERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; private static final char[] SPECIAL_CHARACTERS = { '~', ''', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', '{', ']', '}', '\\', '|', ';', ':', '\'', '"', ',', '<', '.', '>', '/', '?' }; private enum SummerCharacterSets implements PasswordCharacterSet { ALPHA_UPPER(ALPHA_UPPER_CHARACTERS, 1), ALPHA_LOWER(ALPHA_LOWER_CHARACTERS, 1), NUMERIC(NUMERIC_CHARACTERS, 1), SPECIAL(SPECIAL_CHARACTERS, 1); private final char[] chars; private final int minUsage; private SummerCharacterSets(char[] chars, int minUsage) { this.chars = chars; this.minUsage = minUsage; } @Override public char[] getCharacters() { return chars; } @Override public int getMinCharacters() { return minUsage; } } } 

PasswordGenerator.java

 package org.redtown.pw; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Random; public class PasswordGenerator { private final List<PasswordCharacterSet> pwSets; private final char[] allCharacters; private final int minLength; private final int maxLength; private final int presetCharacterCount; public PasswordGenerator(Collection<PasswordCharacterSet> origPwSets, int minLength, int maxLength) { this.minLength = minLength; this.maxLength = maxLength; // Make a copy of the character arrays and min-values so they cannot be changed after initialization int pwCharacters = 0; int preallocatedCharacters = 0; List<PasswordCharacterSet> pwSets = new ArrayList<PasswordCharacterSet>(origPwSets.size()); for(PasswordCharacterSet origpwSet : origPwSets) { PasswordCharacterSet newPwSet = new PwSet(origpwSet); pwSets.add(newPwSet); pwCharacters += newPwSet.getCharacters().length; preallocatedCharacters += newPwSet.getMinCharacters(); } this.presetCharacterCount = preallocatedCharacters; this.pwSets = Collections.unmodifiableList(pwSets); if (minLength < presetCharacterCount) { throw new IllegalArgumentException("Combined minimum lengths " + presetCharacterCount + " are greater than the minLength of " + minLength); } // Copy all characters into single array so we can evenly access all members when accessing this array char[] allChars = new char[pwCharacters]; int currentIndex = 0; for(PasswordCharacterSet pwSet : pwSets) { char[] chars = pwSet.getCharacters(); System.arraycopy(chars, 0, allChars, currentIndex, chars.length); currentIndex += chars.length; } this.allCharacters = allChars; } public char[] generatePassword() { SecureRandom rand = new SecureRandom(); // Set pw length to minLength <= pwLength <= maxLength int pwLength = minLength + rand.nextInt(maxLength - minLength + 1); int randomCharacterCount = pwLength - presetCharacterCount; // Place each index in an array then remove them randomly to assign positions in the pw array List<Integer> remainingIndexes = new ArrayList<Integer>(pwLength); for(int i=0; i < pwLength; ++i) { remainingIndexes.add(i); } // Fill pw array char[] pw = new char[pwLength]; for(PasswordCharacterSet pwSet : pwSets) { addRandomCharacters(pw, pwSet.getCharacters(), pwSet.getMinCharacters(), remainingIndexes, rand); } addRandomCharacters(pw, allCharacters, randomCharacterCount, remainingIndexes, rand); return pw; } private static void addRandomCharacters(char[] pw, char[] characterSet, int numCharacters, List<Integer> remainingIndexes, Random rand) { for(int i=0; i < numCharacters; ++i) { // Get and remove random index from the remaining indexes int pwIndex = remainingIndexes.remove(rand.nextInt(remainingIndexes.size())); // Set random character from character index to pwIndex int randCharIndex = rand.nextInt(characterSet.length); pw[pwIndex] = characterSet[randCharIndex]; } } public static interface PasswordCharacterSet { char[] getCharacters(); int getMinCharacters(); } /** * Defensive copy of a passed-in PasswordCharacterSet */ private static final class PwSet implements PasswordCharacterSet { private final char[] chars; private final int minChars; public PwSet(PasswordCharacterSet pwSet) { this.minChars = pwSet.getMinCharacters(); char[] pwSetChars = pwSet.getCharacters(); // Defensive copy this.chars = Arrays.copyOf(pwSetChars, pwSetChars.length); } @Override public char[] getCharacters() { return chars; } @Override public int getMinCharacters() { return minChars; } } } 
+9
source share

I suggest using apache commons RandomStringUtils. Use what has already been done.

 String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~` !@ #$%^&*()-_=+[{]}\\|;:\'\",<.>/?"; String pwd = RandomStringUtils.random( 15, characters ); System.out.println( pwd ); 

If you use maven

 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> 

otherwise load jar

UPDATE Version with safe random. Therefore, the question of the required symbols is left and can be resolved as in the commentary, generate the required parts separately and normal. Then connect them randomly.

 char[] possibleCharacters = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~` !@ #$%^&*()-_=+[{]}\\|;:\'\",<.>/?")).toCharArray(); String randomStr = RandomStringUtils.random( randomStrLength, 0, possibleCharacters.length-1, false, false, possibleCharacters, new SecureRandom() ); System.out.println( randomStr ); 
+29
source share

Here is a utility that uses only vanilla Java and implements the requirements. This basically gets one of each required character set. Then fills in the remaining random characters from the entire set. Then it mixes it all.

 public class PasswordUtils { static char[] SYMBOLS = (new String("^$*.[]{}()?-\" !@ #%&/\\,><':;|_~'")).toCharArray(); static char[] LOWERCASE = (new String("abcdefghijklmnopqrstuvwxyz")).toCharArray(); static char[] UPPERCASE = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).toCharArray(); static char[] NUMBERS = (new String("0123456789")).toCharArray(); static char[] ALL_CHARS = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789^$*.[]{}()?-\" !@ #%&/\\,><':;|_~'")).toCharArray(); static Random rand = new SecureRandom(); public static String getPassword(int length) { assert length >= 4; char[] password = new char[length]; //get the requirements out of the way password[0] = LOWERCASE[rand.nextInt(LOWERCASE.length)]; password[1] = UPPERCASE[rand.nextInt(UPPERCASE.length)]; password[2] = NUMBERS[rand.nextInt(NUMBERS.length)]; password[3] = SYMBOLS[rand.nextInt(SYMBOLS.length)]; //populate rest of the password with random chars for (int i = 4; i < length; i++) { password[i] = ALL_CHARS[rand.nextInt(ALL_CHARS.length)]; } //shuffle it up for (int i = 0; i < password.length; i++) { int randomPosition = rand.nextInt(password.length); char temp = password[i]; password[i] = password[randomPosition]; password[randomPosition] = temp; } return new String(password); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(getPassword(8)); } } } 
0
source share

Using the random functionality of the java.util package from rt.jar, we can create an arbitrary password of any length. Below is a snippet for the same.

 public class GeneratePassword { public static void main(String[] args) { int length = 10; String symbol = "-/.^&* _!@ %=+>)"; String cap_letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String small_letter = "abcdefghijklmnopqrstuvwxyz"; String numbers = "0123456789"; String finalString = cap_letter + small_letter + numbers + symbol; Random random = new Random(); char[] password = new char[length]; for (int i = 0; i < length; i++) { password[i] = finalString.charAt(random.nextInt(finalString.length())); } System.out.println(password); } 

}

0
source share

All Articles