^ Click Here

Tuesday, January 1, 2013

Java: Generate random password -- with different combinations like characters, numbers, capital letters, special characters.

Alright...I feel that one of the biggest challenge in making computer like humans is the human's unique capacity of being random......Computer mostly do calculations and has to follow some set patterns....however in much programming language there is some way for getting random values.

In web application we often needs to generate random key, number, password or patterns. I thought to write a program which could be a solution to much of the demands, but what it primarily does is to provides you a random password (which was the initial idea) based on the need and pattern we provide.

First the program then the description......

/**
 * 
 */
package com.see.proj.app.util;

/**
 * @author Rahul
 *
 */
public class PassUtil {
 
 private static String ALPHABETS ="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 String CAPITAL_LETTERS = "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 String NUMBERS = "1,2,3,4,5,6,7,8,9,0";
 private static String SPECIAL_CHARACTERS = "~,!,#,$,%,^,&,*,?,-,=,_,+";
 private static int DEFAULT_PASSWORD_LENGTH = 8;
 
 public static String getRandomPassword(int passwordLength, boolean alphabets, boolean numbers, boolean capitalLetters, boolean specialCharacters, String prefix, String suffix, boolean mustContain){
  StringBuilder passContainer = new StringBuilder("");
  
  int desiredTypes = 0;
  //filtering the password possibility string according to option
  passContainer = alphabets?passContainer.append(","+ALPHABETS):passContainer;
  passContainer = numbers?passContainer.append(","+NUMBERS):passContainer;
  passContainer = capitalLetters?passContainer.append(","+CAPITAL_LETTERS):passContainer;
  passContainer = specialCharacters?passContainer.append(","+SPECIAL_CHARACTERS):passContainer;
  
  //If no types are allowed i.e. all desired types options are false
  if("".equals(passContainer.toString())){
   throw new IllegalArgumentException("There must be some type out of small letters, capital letters, numbers or special character must be allowed");
  }
  //typically for mustContain option
  desiredTypes = alphabets?(desiredTypes+1):desiredTypes;
  desiredTypes = numbers?(desiredTypes+1):desiredTypes;
  desiredTypes = capitalLetters?(desiredTypes+1):desiredTypes;
  desiredTypes = specialCharacters?(desiredTypes+1):desiredTypes;

  //getting all the characters which can be in the password as an array
  String[] passContainerArr = passContainer.toString().substring(1).split(",");
  
  //validating the suffix and prefix
  String passPrefix = prefix ==null?"":prefix;
  String passSuffix = suffix ==null?"":suffix;
  
  //typically for mustContain option
  String externalPass = passPrefix+passSuffix;
  boolean haveAlphabet = ("").equals(externalPass)?false:containsSmallLetter(externalPass);
  boolean haveCapitalLetters = ("").equals(externalPass)?false:containsCapitalLetter(externalPass);
  boolean haveNumbers = ("").equals(externalPass)?false:containsNumber(externalPass);
  boolean haveSpecialCharacter = ("").equals(externalPass)?false:containsSpecialCharacter(externalPass);

  
  //validating password length
  int passLength = passwordLength==0?DEFAULT_PASSWORD_LENGTH:passwordLength;
  int passLengthToFillIn = passLength - (passPrefix.length()+passSuffix.length());
  if(passLengthToFillIn == 0){
   return passPrefix+passSuffix;
  }
  else if(passLengthToFillIn < 0){
   throw new IllegalArgumentException("Password prefix : "+passPrefix+" and suffix : "+passSuffix+" adds up to more than the password length : "+passwordLength+" provided");
  }
  
  if(mustContain && passLengthToFillIn < desiredTypes){
   throw new IllegalArgumentException("password cannot contain all the character since desired type : "+desiredTypes+" is more than password length : "+passLengthToFillIn+" to fill in");
  }
  StringBuilder pass = new StringBuilder("");
  //creating the password sans prefix and suffix in a loop
  for(int midPass=0; midPass<passLengthToFillIn; midPass++){
   //this part is optional and experimental with must include all the desired character : Start
   if(mustContain && desiredTypes >= (passLengthToFillIn - midPass)){
    if(alphabets && !haveAlphabet && !containsSmallLetter(pass.toString())){
     pass.append(getSingleRandomSmallLetter());
     haveAlphabet = true;
     continue;
    }
    if(capitalLetters && !haveCapitalLetters && !containsCapitalLetter(pass.toString())){
     pass.append(getSingleRandomCapitalLetter());
     haveCapitalLetters = true;
     continue;
    }
    if(numbers && !haveNumbers && !containsNumber(pass.toString())){
     pass.append(getSingleRandomNumber());
     haveNumbers = true;
     continue;
    }
    if(specialCharacters && !haveSpecialCharacter && !containsSpecialCharacter(pass.toString())){
     pass.append(getSingleRandomSpecialCharacter());
     haveSpecialCharacter = true;
     continue;
    }
   }
   //End
   
   //the actual creation of password string
   int randomIndex = (int) Math.floor(Math.random()*passContainerArr.length);
   //appending a random character from password array
   pass.append(passContainerArr[randomIndex]);
  }
  //final password
  String totalPass = passPrefix+pass.toString()+passSuffix;
  return totalPass;
 }
 
 private static boolean containsSmallLetter(String stringToCheck){
  return stringToCheck.matches("(.*)[a-z](.*)");
 }
 
 private static boolean containsCapitalLetter(String stringToCheck){
  return stringToCheck.matches("(.*)[A-Z](.*)");
 }
 
 private static boolean containsNumber(String stringToCheck){
  return stringToCheck.matches("(.*)[0-9](.*)");
 }
 
 private static boolean containsSpecialCharacter(String stringToCheck){
  String[] spChArray = SPECIAL_CHARACTERS.split(",");
  for(String spCharacter : spChArray){
   if(stringToCheck.indexOf(spCharacter) >= 0){
    return true;
   }
  }
  return false;
 }
 
 private static String getSingleRandomSmallLetter(){
  String[] alphabetArr = ALPHABETS.split(",");
  return alphabetArr[(int) Math.floor(Math.random()*alphabetArr.length)];
 }
 
 private static String getSingleRandomCapitalLetter(){
  String[] alphabetArr = CAPITAL_LETTERS.split(",");
  return alphabetArr[(int) Math.floor(Math.random()*alphabetArr.length)];
 }
 
 private static String getSingleRandomNumber(){
  String[] alphabetArr = NUMBERS.split(",");
  return alphabetArr[(int) Math.floor(Math.random()*alphabetArr.length)];
 }
 
 private static String getSingleRandomSpecialCharacter(){
  String[] alphabetArr = SPECIAL_CHARACTERS.split(",");
  return alphabetArr[(int) Math.floor(Math.random()*alphabetArr.length)];
 }
 
 
 private static String generateRandomNumberCode(int lengthOfDigit){
  //mustContain option is meaningless since only number option is to be there in the code/password
  return getRandomPassword(lengthOfDigit,false,true,false,false,null,null,false);
 }
 
 private static String getRandomPasswordStartingWithCapitalAndEndingWithNumber(int passwordLength){
  String prefix = getSingleRandomCapitalLetter();
  String suffix = getSingleRandomNumber();
  return getRandomPassword(passwordLength, true, true, true, true, prefix, suffix, false);
 }
 
 private static String getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter(int passwordLength){
  String prefix = getSingleRandomCapitalLetter();
  String suffix = getSingleRandomSpecialCharacter();
  return getRandomPassword(passwordLength, true, true, true, true, prefix, suffix, false);
 }
 
 private static String getRandomAlphaNumericPasswordStartingWithNumber(int passwordLength){
  String prefix = getSingleRandomNumber();
  return getRandomPassword(passwordLength, true, true, false, false, prefix, null, false);
 }
 
 public static void main(String[] args){

  //must contain all types
  String allPassMustContain = getRandomPassword(4,true,true,true,true,null,null,true);
  System.out.println("allPassMustContain : "+allPassMustContain);
  //all options could be there in the password
  String allPass = getRandomPassword(10,true,true,true,true, null,"99",false);
  System.out.println("allPass : "+allPass);
  //only small letter alphabet and number
  String alphaNumeric = getRandomPassword(10,true,true,false,false,null,null,false);
  System.out.println("alphaNumeric : "+alphaNumeric);
  //alphabet including capital letter and numbers
  String alphaNumericWithCapital = getRandomPassword(10,true,true,true,false,null,null,false);
  System.out.println("alphaNumericWithCapital : "+alphaNumericWithCapital);
  //numbers only
  String numberOnly = getRandomPassword(16,false,true,false,false,null,null,false);
  System.out.println("numberOnly : "+numberOnly);
  //random number only
  System.out.println("generateRandomNumberCode : "+generateRandomNumberCode(5));
  //Methods with a pattern
  System.out.println("getRandomPasswordStartingWithCapitalAndEndingWithNumber : "+getRandomPasswordStartingWithCapitalAndEndingWithNumber(8));
  System.out.println("getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter : "+getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter(8));
  System.out.println("getRandomAlphaNumericPasswordStartingWithNumber : "+getRandomAlphaNumericPasswordStartingWithNumber(8));
 }
}

before we move further let me show you what it actually prints on running

allPassMustContain : aS2_
allPass : uFttRDOm99
alphaNumeric : nqalid32i5
alphaNumericWithCapital : 7r4uCYFxCY
numberOnly : 2779697529864423
generateRandomNumberCode : 09154
getRandomPasswordStartingWithCapitalAndEndingWithNumber : J#kUpsg8
getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter : TU0Ab&f~
getRandomAlphaNumericPasswordStartingWithNumber : 2w7uqwpw

 // in a nutshell
First one  allPassMustContain is a 4 letter random password which must contains a small letter, a capital letter, a number and a special character
Second allPass is a 10 letter random password which can contain small letters, capital letters, numbers and special characters and always ends with 99.
Third is  alphaNumeric a 10 letter random password which contains small letters and numbers only
Fourth  alphaNumericWithCapital is a 10 letter random password which contains letters (small and capital) and numbers only
Fifth numberOnly is a 16 digit random number.
Sixth generateRandomNumberCode is 5 digit random number.
Seventh getRandomPasswordStartingWithCapitalAndEndingWithNumber defines itself  8 character random password starts with capital letter and end with numbers.
so is with getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter 8 character random password starts with capital letter and end with special character.
And the last Ninth getRandomAlphaNumericPasswordStartingWithNumber one produces 8 character random password which always starts with a number.

Alright the idea was to create one static method which is flexible and capable of providing the random password with different needs so first of all I chose four factors - small letters (a-z), Capital letters (A-Z), numbers (0-9), Special characters (#,% etc.) hence there are four static variable with comma(,) separated values :

 private static String ALPHABETS ="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 String CAPITAL_LETTERS = "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 String NUMBERS = "1,2,3,4,5,6,7,8,9,0";
 private static String SPECIAL_CHARACTERS = "~,!,#,$,%,^,&,*,?,-,=,_,+";
 private static int DEFAULT_PASSWORD_LENGTH = 8;

the last one DEFAULT_PASSWORD_LENGTH is little unimportant right now, now initially the function I came out was :

public static String getRandomPassword(int passwordLength, boolean alphabets, boolean numbers, boolean capitalLetters, boolean specialCharacters, String prefix, String suffix){
  StringBuilder passContainer = new StringBuilder("");
  
  //filtering the password possibility string according to option
  passContainer = alphabets?passContainer.append(ALPHABETS):passContainer;
  passContainer = numbers?passContainer.append(NUMBERS):passContainer;
  passContainer = capitalLetters?passContainer.append(CAPITAL_LETTERS):passContainer;
  passContainer = specialCharacters?passContainer.append(SPECIAL_CHARACTERS):passContainer;
  
  //getting all the characters which can be in the password as an array
  String[] passContainerArr = passContainer.toString().split(",");
  
  //validating the suffix and prefix
  String passPrefix = prefix ==null?"":prefix;
  String passSuffix = suffix ==null?"":suffix;
  
  //validating password length
  int passLength = passwordLength==0?DEFAULT_PASSWORD_LENGTH:passwordLength;
  int passLengthToFillIn = passLength - (passPrefix.length()+passSuffix.length());
  if(passLengthToFillIn == 0){
   return passPrefix+passSuffix;
  }
  else if(passLengthToFillIn < 0){
   throw new IllegalArgumentException("Password prefix : "+passPrefix+" and suffix : "+passSuffix+" adds up to more than the password length : "+passwordLength+" provided");
  }
  StringBuilder pass = new StringBuilder("");
  //creating the password sans prefix and suffix in a loop
  for(int midPass=0; midPass<passLengthToFillIn; midPass++){
   int randomIndex = (int) Math.floor(Math.random()*passContainerArr.length);
   //appending a random character from password array
   pass.append(passContainerArr[randomIndex]);
  }
  //final password
  String totalPass = passPrefix+pass.toString()+passSuffix;
  return totalPass;
 }

In most cases this method itself was enough to tackle the problem, the parameter it takes is
 > passwordLength : Simple enough, the (int) length of password which is needed (it's the final length including prefix and suffix provided)
 > alphabets : the boolean which specifies if the password should/can consist alphabet or not (if true it can).
 > numbers : same as alphabets i.e. if numbers should be considered or not.
 > capitalLetters : capital letters or not.
 > specialCharacter : make a wild guess :) .
 > prefix : if any special prefix (at the beginning) you want to provide in your password like 'P1' etc.
 > suffix : at the end

Now based on the provided options (the boolean value) first of all I created a string of all the comma separated value which can be there and with that created an array of single characters of all those character by splitting that.
checked for the validity of passwordLength, prefix and suffix and calculated actually how many random characters we need barring the prefix and suffix.

simply ran the loop that many time and got a random character from the array and added it to create a middle password and finally returned the total password with adding prefix and suffix it in the start and end respectively.

and it was done. Mission Accomplished!!

all you need to do is provide your options for ex.

int PasswordLength = 9; 
boolean containsAlphabet = true;
boolean containsNumber = true;
boolean containsCapitalLetters = false;
boolean containsSpecialCharacters = false
String suffix = null;
String prefix = "7";
String randomPassword = PassUtil.getRandomPassword(passwordLength, containsAlphabet , containsNumber , containsCapitalLetters , containsSpecialCharacters , prefix, suffix);

Yeah I know initiating that much variable is unnecessary but I did that on purpose because it makes the code readable and easy to understand. if I simply write

PassUtil.getRandomPassword(9, true, true, false, false, "7", null);

after few months I myself won't know what the hell those true, false and null stands for until I further go into that very method and try to recollect.

anyway either way it would return you a 9 character password starting with 7 and will contain small letters and numbers (i.e.alphanumeric). So we can try different combinations.

But there is a problem, a-z is 26 character and 0-9 is only 10 character, there are possible chances that your password may contain all the letters only and no number after all it's a random selection. In fact there might be a chance that the random password may contain only numbers and no character. If we include A-Z then there are 26 more characters making the letter to 52 and numbers 10 and special character well 13 i.e unequal proportion.

Obviously there is a scenario where we need that our password must contain at least one character or numbers or any option we have allowed in our password that is where it got bit complicated as I introduced a new boolean value mustContain which forces the method to put atleast one of each type. (look at the final method at the top)

Of course it's a added burden on the very method because now I need to check for all the allowed types in the password and then check that let's say when few minimum password is needed to be added and when there is not even a single character of the desired type I am putting a random value and continuing the loop, obviously it needs to be done before it puts a character from the collection of all desired types array. I have introduced some extra boolean values (haveNumbers, haveCapitalLetters etc) so that once we put the character of a certain type it does not check for the same type again.

Finally we can define our own pattern specific random password generator for example  getRandomPasswordStartingWithCapitalAndEndingWithSpecialCharacter

 All we need to do is to use the different combinations of the core method :
getRandomPassword(int passwordLength, boolean alphabets, boolean numbers, boolean capitalLetters, boolean specialCharacters, String prefix, String suffix, boolean mustContain)

Ohh and don't forget to check your password strength in  -->>here<<--

Hope it helps somebody.

 P.S - Please have a long password with at least one of each type character. :)