I would like to create a password generator that create password according to the restriction set by user. The restrictions is:
- Minimum Password Length
- Maximum Password Length
- Minimum Letter And Digit
- Minimum Letter
- Minimum Uppercase Letter
- Minimum Lowercase Letter
- Minimum Digit
- 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.
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
.
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
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).
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();
}