1

I would like to create a password generator that create password according to the restriction set by user. The restrictions is:

  1. Minimum Password Length
  2. Maximum Password Length
  3. Minimum Letter And Digit
  4. Minimum Letter
  5. Minimum Uppercase Letter
  6. Minimum Lowercase Letter
  7. Minimum Digit
  8. Maximum Repeat Character

I lookout through google and most of the example code doesn't meet the requirement that I need. So I improvise the code like this:

private char[] GeneratePassword(int minLength, int maxLength,
        int maxRepeatCharacter, int minLetterAndDigit, int minLetter,
        int minLowerCaseLetter, int minUpperCaseLetter, int minDigit) {

    final String LETTER = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    final String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    final String LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
    final String DIGIT = "0123456789";
    final String[] randomSelector = {LETTER,UPPERCASE,LOWERCASE,DIGIT};

    int len = getRandomNumber(minLength, maxLength);
    char[] passwordGenerated = new char[len];
    char[] characterUsed = new char[len];
    int selection;
    int letterAndDigitUsed = 0;
    int letterUsed = 0;
    int lowerCaseLetterUsed = 0;
    int upperCaseLetterUsed = 0;
    int digitUsed = 0;
    int index = 0;

    if (minLength > maxLength) {
      // throw new IllegalArgumentException("Min.Length > Max.Length!");
    }

    if (minLetter + minDigit > minLetterAndDigit) {
      // throw new IllegalArgumentException("Error!");
    }

    while (index != len) {
        selection = getRandomNumber(0, randomSelector.length);
        if (selection == 0) {
            passwordGenerated[index] = LETTER.charAt(RandomUtils.nextInt(0,
                    LETTER.length()));
            if (checkRepeatCharacter(passwordGenerated[index],
                    characterUsed, index, maxRepeatCharacter) == false) {
                characterUsed[index] = passwordGenerated[index];
                index++;
                letterUsed++;
                letterAndDigitUsed++;
                break;
            }
        } else if (selection == 1) {
            passwordGenerated[index] = UPPERCASE.charAt(RandomUtils
                    .nextInt(0, UPPERCASE.length()));
            if (checkRepeatCharacter(passwordGenerated[index],
                    characterUsed, index, maxRepeatCharacter) == false) {
                characterUsed[index] = passwordGenerated[index];
                index++;
                upperCaseLetterUsed++;
                letterAndDigitUsed++;
                break;
            }
        } else if (selection == 2) {
            passwordGenerated[index] = LOWERCASE.charAt(RandomUtils
                    .nextInt(0, LOWERCASE.length()));
            if (checkRepeatCharacter(passwordGenerated[index],
                    characterUsed, index, maxRepeatCharacter) == false) {
                characterUsed[index] = passwordGenerated[index];
                index++;
                lowerCaseLetterUsed++;
                letterAndDigitUsed++;
                break;
            }
        } else if (selection == 3) {
            passwordGenerated[index] = DIGIT.charAt(RandomUtils.nextInt(0,
                    DIGIT.length()));
            if (checkRepeatCharacter(passwordGenerated[index],
                    characterUsed, index, maxRepeatCharacter) == false) {
                characterUsed[index] = passwordGenerated[index];
                index++;
                digitUsed++;
                letterAndDigitUsed++;
                break;
            }
        }
    }

    return passwordGenerated;
}

private boolean checkRepeatCharacter(char passwordGenerated,
        char[] passwordUsed, int index, int maxRepeatCharacter) {
    int characterRepeated = 0;
    for (int i = 0; i < index; i++) {
        if (String.valueOf(passwordUsed[i]).equals(
                String.valueOf(passwordGenerated))) {
            characterRepeated++;
            if (characterRepeated == maxRepeatCharacter) {
                return true;
            }
        }
    }
    return false;
}

private int getRandomNumber(int minLength, int maxLength) {
    Random r = new Random();
    return r.nextInt(maxLength - minLength) + minLength;
}

The problem that I'm having is how to ensure the minimum condition are met. At the same time, I dont want my password to be generated by repeating at the same type of character.

  1. Example: If I put my maximum password length to 10 and I want a minimum digit of 5. I prefer to have 1jP2k3o4m9 rather than 57812aJ9tP.

  2. 2nd Example: If I put my maximum password length to 5 and I want a minimum lowercase letter of 3. I prefer to have Pj9mn rather than jkl5V.

As you can see, the 2nd generated password is trying to fullfill the minimum requirement first, then only do a random selection to other character type. Which will make the password more vulnerable. Is there any way to do this algorithm.

Ref: Generating a Random Password with Restrictions in Java

Mohd Fikrie
  • 197
  • 4
  • 21
  • `3` **is not** a lowercase letter. – Elliott Frisch Nov 04 '15 at 02:14
  • @ElliottFrisch which part are you referring to? – Mohd Fikrie Nov 04 '15 at 02:19
  • *I put my maximum password length to 5 and I want a minimum lowercase letter of 3* – Elliott Frisch Nov 04 '15 at 02:20
  • @ElliottFrisch Yes, 3 is not a lowercase letter. Sorry if my english is bad and causes confusion. What I mean on that phrase is to use at least 3 lowercase letter. – Mohd Fikrie Nov 04 '15 at 02:23
  • I would first ask why? Any generated password is already a security risk. If this is for an initial password or password reset system, there are other ways to accomplish those. – user207421 Nov 04 '15 at 02:28
  • 1
    @EJP I do not have the detail answer for that question. Its a requirement from the customer. I just know they said its for security reason and they want it for hardening. Can u suggest me the other way that you mention. I would love to take a look. – Mohd Fikrie Nov 04 '15 at 02:43

2 Answers2

3

I would take a look at something like vt-password:

https://code.google.com/p/vt-middleware/wiki/vtpassword

In addition to providing flexible mechanisms for working with password validation based on some fairly standard required characteristics, it provides support for generating passwords that meet the conditions defined by a list of "character rules":

https://code.google.com/p/vt-middleware/wiki/vtpassword#Generating_passwords

Worth noting that the project has graduated from incubation and is now known as Passay - http://www.passay.org/.

The current password generator JavaDocs don't reflect the same set of character rules that were available with vt-password. You could either run with vt-password for a time, or provide your own CharacterRule implementations to work with the Passay generator (+ validator, if also required).

Will Hogan
  • 909
  • 4
  • 9
1

I have fixed my code. So far I've tested, I works perfectly as required. I leave this code just in case someone else need it in the future.

private boolean checkRepeatCharacter(char randomCharacter,
        ArrayList<Character> passwordUsed, int usedLength,
        int maxRepeatCharacter) {
    int characterRepeated = 0;
    for (int i = 0; i < usedLength; i++) {
        if (String.valueOf(passwordUsed.get(i)).equals(
                String.valueOf(randomCharacter))) {
            characterRepeated++;
            if (characterRepeated == maxRepeatCharacter) {
                return false;
            }
        }
    }
    return true;
}

private boolean checkUsedIndex(int index, ArrayList<Integer> usedIndex) {
    for (int i = 0; i < usedIndex.size(); i++) {
        if (usedIndex.contains(index)) {
            return false;
        }
    }
    return true;
}

private int getRandomNumber(int minLength, int maxLength) {
    Random r = new Random();
    return r.nextInt(maxLength - minLength) + minLength;
}

public String generatePassword(int minLength, int maxLength,
        int maxRepeatCharacter, int minLetterAndDigit, int minLetter,
        int minLowerCaseLetter, int minUpperCaseLetter, int minDigit,
        int minSpecialCharacter, String specialCharacter) {

    final String LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
    final String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    final String DIGIT = "0123456789";
    StringBuilder ALL = new StringBuilder();
    ALL.append(LOWERCASE);
    ALL.append(UPPERCASE);
    ALL.append(DIGIT);
    ALL.append(specialCharacter);
    ALL.toString();

    char getRandom;
    int length = 0;
    StringBuilder passwordGenerated = new StringBuilder();
    ArrayList<Character> characterUsed = new ArrayList<Character>();
    ArrayList<Integer> indexUsed = new ArrayList<Integer>();

    int passwordLength = 0;
    int lowerCaseLetterUsed = 0;
    int upperCaseLetterUsed = 0;
    int letterUsed = 0;
    int digitUsed = 0;
    int letterAndDigitUsed = 0;
    int specialCharacterUsed = 0;

    if (minLength > maxLength) {
        throw new IllegalArgumentException("Min. Length > Max. Length!");
    }

    if (minUpperCaseLetter + minLowerCaseLetter > minLetter) {
        throw new RuntimeException(
                "mininimum Lower Case + Minimum Uppercase cannot exceed minLetter");
    }
    if (minLetter + minDigit > minLetterAndDigit) {
        throw new RuntimeException(
                "mininimum Letter + Minimum Digit cannot exceed minimum Letter And Digit");
    }
    if (minLetter + minDigit + minSpecialCharacter > maxLength) {
        throw new RuntimeException(
                "minimum Digit + minimum Letter + Minimum Special Character cannot excced maximum Length");
    }

    while ((length < minLetter) && (length < minLetterAndDigit)) {
        length = getRandomNumber(minLength, maxLength);
    }

    while (passwordLength != length) {
        while (letterAndDigitUsed < minLetterAndDigit) {
            while (letterUsed < minLetter) {
                lowerCaseLetterUsed = 0;
                for (int i = 0; lowerCaseLetterUsed < minLowerCaseLetter; i++) {
                    int index = getRandomNumber(0, length);
                    if (checkUsedIndex(index, indexUsed) == true) {
                        getRandom = LOWERCASE.charAt(getRandomNumber(0,
                                LOWERCASE.length()));
                        if (checkRepeatCharacter(getRandom, characterUsed,
                                characterUsed.size(), maxRepeatCharacter) == true) {
                            passwordGenerated.append(getRandom);
                            characterUsed.add(getRandom);
                            indexUsed.add(index);
                            lowerCaseLetterUsed++;
                            letterUsed++;
                            letterAndDigitUsed++;
                            passwordLength++;
                            if (letterUsed == minLetter) {
                                break;
                            }
                        }
                    }
                }
                if (letterAndDigitUsed == minLetterAndDigit) {
                    break;
                }
                upperCaseLetterUsed = 0;
                for (int i = 0; upperCaseLetterUsed < minUpperCaseLetter; i++) {
                    int index = getRandomNumber(0, length);
                    if (checkUsedIndex(index, indexUsed) == true) {
                        getRandom = UPPERCASE.charAt(getRandomNumber(0,
                                UPPERCASE.length()));
                        if (checkRepeatCharacter(getRandom, characterUsed,
                                characterUsed.size(), maxRepeatCharacter) == true) {
                            passwordGenerated.append(getRandom);
                            characterUsed.add(getRandom);
                            indexUsed.add(index);
                            lowerCaseLetterUsed++;
                            letterUsed++;
                            letterAndDigitUsed++;
                            passwordLength++;
                            if (letterUsed == minLetter) {
                                break;
                            }
                        }
                    }
                }
                if (letterAndDigitUsed == minLetterAndDigit) {
                    break;
                }
            }
            for (int i = 0; digitUsed < minDigit; i++) {
                int index = getRandomNumber(0, length);
                if (checkUsedIndex(index, indexUsed) == true) {
                    getRandom = DIGIT.charAt(getRandomNumber(0,
                            DIGIT.length()));
                    if (checkRepeatCharacter(getRandom, characterUsed,
                            characterUsed.size(), maxRepeatCharacter) == true) {
                        passwordGenerated.append(getRandom);
                        characterUsed.add(getRandom);
                        indexUsed.add(index);
                        digitUsed++;
                        letterAndDigitUsed++;
                        passwordLength++;
                    }
                }
            }
        }
        for (int i = 0; specialCharacterUsed < minSpecialCharacter; i++) {
            if (checkUsedIndex(i, indexUsed) == true) {
                getRandom = specialCharacter.charAt(getRandomNumber(0,
                        specialCharacter.length()));
                if (checkRepeatCharacter(getRandom, characterUsed,
                        characterUsed.size(), maxRepeatCharacter) == true) {
                    passwordGenerated.append(getRandom);
                    characterUsed.add(getRandom);
                    indexUsed.add(i);
                    specialCharacterUsed++;
                    passwordLength++;
                }
            }
        }
        for (int i = 0; i < length; i++) {
            if (checkUsedIndex(i, indexUsed) == true) {
                getRandom = ALL.charAt(getRandomNumber(0, ALL.length()));
                if (checkRepeatCharacter(getRandom, characterUsed,
                        characterUsed.size(), maxRepeatCharacter) == true) {
                    passwordGenerated.append(getRandom);
                    characterUsed.add(getRandom);
                    indexUsed.add(i);
                    passwordLength++;
                }
            }
        }
    }
    return passwordGenerated.toString();
}
Mohd Fikrie
  • 197
  • 4
  • 21