RandPass.java Source Code

  • RandPass Documentation and Examples
  • RandPass Javadoc
    /*
     * Generate random passwords.
     * Copyright (C) 2001-2010 Stephen Ostermiller
     * http://ostermiller.org/contact.pl?regarding=Java+Utilities
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * See LICENSE.txt for details.
     */
    
    package com.Ostermiller.util;
    
    import java.security.SecureRandom;
    import java.text.MessageFormat;
    import java.util.ResourceBundle;
    import java.util.Locale;
    import java.util.Vector;
    
    /**
     * Generates a random String using a cryptographically
     * secure random number generator.
     * <p>
     * The alphabet (characters used in the passwords generated)
     * may be specified, and the random number generator can be
     * externally supplied.
     * <p>
     * Care should be taken when using methods that limit the types
     * of passwords may be generated.  Using an alphabet that is too
     * small, using passwords that are too short, requiring too many
     * of a certain type of character, or not allowing repetition,
     * may decrease security.
     * <p>
     * More information about this class is available from <a target="_top" href=
     * "http://ostermiller.org/utils/RandPass.html">ostermiller.org</a>.
     *
     * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
     * @since ostermillerutils 1.00.00
     */
    public class RandPass {
    
    	/**
    	 * Version number of this program
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final String version = "1.1";
    
    	/**
    	 * Locale specific strings displayed to the user.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected static ResourceBundle labels = ResourceBundle.getBundle("com.Ostermiller.util.RandPass",  Locale.getDefault());
    
    
    	/**
    	 * Default length for passwords
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	private static final int DEFAULT_PASSWORD_LENGTH = 8;
    
    	/**
    	 * Alphabet consisting of upper and lower case letters A-Z and
    	 * the digits 0-9.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] NUMBERS_AND_LETTERS_ALPHABET = {
    		'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','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','0','1','2','3',
    		'4','5','6','7','8','9',
    	};
    
    	/**
    	 * Alphabet consisting of all the printable ASCII symbols.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] SYMBOLS_ALPHABET = {
    		'!','\"','#','$','%','&','\'','(',
    		')','*','+',',','-','.','/',':',
    		';','<','?','@','[','\\',']','^',
    		'_','`','{','|','}','~',
    	};
    
    	/**
    	 * Alphabet consisting of all the printable ASCII characters.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] PRINTABLE_ALPHABET = {
    		'!','\"','#','$','%','&','\'','(',
    		')','*','+',',','-','.','/','0',
    		'1','2','3','4','5','6','7','8',
    		'9',':',';','<','?','@','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',
    		'[','\\',']','^','_','`','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',
    		'{','|','}','~',
    	};
    
    	/**
    	 * Alphabet consisting of the lower case letters A-Z.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] LOWERCASE_LETTERS_ALPHABET = {
    		'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',
    	};
    
    	/**
    	 * Alphabet consisting of the lower case letters A-Z and
    	 * the digits 0-9.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] LOWERCASE_LETTERS_AND_NUMBERS_ALPHABET = {
    		'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','0','1','2','3','4','5',
    		'6','7','8','9',
    	};
    
    	/**
    	 * Alphabet consisting of upper and lower case letters A-Z.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] LETTERS_ALPHABET = {
    		'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','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',
    	};
    
    	/**
    	 * Alphabet consisting of the upper letters A-Z.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] UPPERCASE_LETTERS_ALPHABET = {
    		'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',
    	};
    
    	/**
    	 * Alphabet consisting of upper and lower case letters A-Z and
    	 * the digits 0-9 but with characters that are often mistaken
    	 * for each other when typed removed. (I,L,O,U,V,i,l,o,u,v,0,1)
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final char[] NONCONFUSING_ALPHABET = {
    		'A','B','C','D','E','F','G','H',
    		'J','K','M','N','P','Q','R','S',
    		'T','W','X','Y','Z','a','b','c',
    		'd','e','f','g','h','j','k','m',
    		'n','p','q','r','s','t','w','x',
    		'y','z','2','3','4','5','6','7',
    		'8','9',
    	};
    
    	/**
    	 * Random number generator used.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected SecureRandom rand;
    
    	/**
    	 * One less than the maximum number of repeated characters
    	 * that are allowed in a password.
    	 * Set to -1 to disable this feature.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected int repetition = -1;
    
    	/**
    	 * Set of characters which may be
    	 * used in the generated passwords.
    	 * <p>
    	 * This value may not be null or have
    	 * no elements.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected char[] alphabet;
    
    	/**
    	 * Set of characters which may be
    	 * used for the first character
    	 * in the generated passwords.
    	 * <p>
    	 * This value may be null but it must
    	 * have at least one element otherwise.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected char[] firstAlphabet;
    
    	/**
    	 * Set of characters which may be
    	 * used for the last character
    	 * in the generated passwords.
    	 * <p>
    	 * This value may be null but it must
    	 * have at least one element otherwise.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected char[] lastAlphabet;
    
    	/**
    	 * Create a new random password generator
    	 * with the default secure random number generator
    	 * and default NONCONFUSING alphabet for all characters.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public RandPass(){
    		this(new SecureRandom(), NONCONFUSING_ALPHABET);
    	}
    
    	/**
    	 * Create a new random password generator
    	 * with the given secure random number generator
    	 * and default NONCONFUSING alphabet for all characters.
    	 *
    	 * @param rand Secure random number generator to use when generating passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public RandPass(SecureRandom rand){
    		this(rand, NONCONFUSING_ALPHABET);
    	}
    
    	/**
    	 * Create a new random password generator
    	 * with the default secure random number generator
    	 * and given alphabet for all characters.
    	 *
    	 * @param alphabet Characters allowed in generated passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public RandPass(char[] alphabet){
    		this(new SecureRandom(), alphabet);
    	}
    
    	/**
    	 * Create a new random password generator
    	 * with the given secure random number generator
    	 * and given alphabet for all characters.
    	 *
    	 * @param rand Secure random number generator to use when generating passwords.
    	 * @param alphabet Characters allowed in generated passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public RandPass(SecureRandom rand, char[] alphabet){
    		this.rand = rand;
    		this.alphabet = alphabet;
    	}
    
    	private class Requirement {
    		private Requirement(char[] alphabet, int num){
    			this.alphabet = alphabet;
    			this.num = num;
    		}
    		private char[] alphabet;
    		private int num;
    	}
    
    	private enum RandPassCmdLnOption {
    		/** --help */
    		HELP(new CmdLnOption(labels.getString("help.option")).setDescription( labels.getString("help.message"))),
    		/** --version */
    		VERSION(new CmdLnOption(labels.getString("version.option")).setDescription(labels.getString("version.message"))),
    		/** --about */
    		ABOUT(new CmdLnOption(labels.getString("about.option")).setDescription(labels.getString("about.message"))),
    		/** --alphabet */
    		ALPHABET(new CmdLnOption(labels.getString("alphabet.option"), 'a').setRequiredArgument().setDescription(labels.getString("a.message"))),
    		/** --first */
    		FIRST(new CmdLnOption(labels.getString("first.alphabet.option"), 'F').setRequiredArgument().setDescription(labels.getString("F.message"))),
    		/** --last */
    		LAST(new CmdLnOption(labels.getString("last.alphabet.option"), 'L').setRequiredArgument().setDescription(labels.getString("L.message"))),
    		/** --number */
    		NUMBER(new CmdLnOption(labels.getString("number.option"), 'n').setRequiredArgument().setDescription(labels.getString("n.message"))),
    		/** --reps */
    		REPS(new CmdLnOption(labels.getString("maxrep.option"), 'r').setRequiredArgument().setDescription(labels.getString("r.message"))),
    		/** --length */
    		LENGTH(new CmdLnOption(labels.getString("length.option"), 'l').setRequiredArgument().setDescription(labels.getString("l.message"))),
    		/** --require */
    		REQUIRE(new CmdLnOption(labels.getString("require.option"), 'R').setRequiredArgument().setDescription(labels.getString("R.message"))),
    		/** --verify */
    		VERIFY(new CmdLnOption(labels.getString("verify.option"), 'v').setRequiredArgument().setDescription(labels.getString("v.message")));
    
    		private CmdLnOption option;
    
    		private RandPassCmdLnOption(CmdLnOption option){
    			option.setUserObject(this);
    			this.option = option;
    		}
    
    		private CmdLnOption getCmdLineOption(){
    			return option;
    		}
    	}
    
    	/**
    	 * Generate a random passwords.
    	 * Run with --help argument for more information.
    	 *
    	 * @param args Command line arguments.
    	 * @throws Exception errors
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static void main(String[] args) throws Exception {
    		CmdLn commandLine = new CmdLn(
    			args
    		).setDescription(
    			labels.getString("randpass") + labels.getString("purpose.message")
    		);
    		for (RandPassCmdLnOption option: RandPassCmdLnOption.values()){
    			commandLine.addOption(option.getCmdLineOption());
    		}
    		int number = 1;
    		char[] alphabet = NONCONFUSING_ALPHABET;
    		char[] firstAlphabet = null;
    		char[] lastAlphabet = null;
    		Vector<String> reqs = new Vector<String>();
    		Vector<String> ver = new Vector<String>();
    		int maxreps = 0;
    		int length = 8;
    		for(CmdLnResult result: commandLine.getResults()){
    			switch((RandPassCmdLnOption)result.getOption().getUserObject()){
    				case HELP:{
    					// print out the help message
    					commandLine.printHelp();
    					System.exit(0);
    				} break;
    				case VERSION:{
    					// print out the version message
    					System.out.println(MessageFormat.format(labels.getString("version"), (Object[])new String[] {version}));
    					System.exit(0);
    				} break;
    				case ABOUT:{
    					System.out.println(
    						labels.getString("randpass") + " -- " + labels.getString("purpose.message") + "\n" +
    						MessageFormat.format(labels.getString("copyright"), (Object[])new String[] {"2001-2007", "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)"}) + "\n\n" +
    						labels.getString("license")
    					);
    					System.exit(0);
    				} break;
    				case ALPHABET:{
    					String alph = result.getArgument();
    					if (alph.length() == 0){
    						alphabet = NONCONFUSING_ALPHABET;
    					} else {
    						alphabet = alph.toCharArray();
    					}
    				} break;
    				case FIRST:{
    					String alph = result.getArgument();
    					if (alph.length() == 0){
    						firstAlphabet = null;
    					} else {
    						firstAlphabet = alph.toCharArray();
    					}
    				} break;
    				case LAST:{
    					String alph = result.getArgument();
    					if (alph.length() == 0){
    						lastAlphabet = null;
    					} else {
    						lastAlphabet = alph.toCharArray();
    					}
    				} break;
    				case REQUIRE:{
    					String alph = result.getArgument();
    					if (alph.length() != 0){
    						reqs.add(alph);
    					}
    				} break;
    				case VERIFY:{
    					ver.add(result.getArgument());
    				} break;
    				case NUMBER:{
    					try {
    						number = Integer.parseInt(result.getArgument());
    						if (number <= 0) throw new NumberFormatException();
    					} catch (NumberFormatException nfe){
    						System.err.println(labels.getString("number.bad_argument"));
    						System.exit(0);
    					}
    				} break;
    				case REPS:{
    					try {
    						maxreps = Integer.parseInt(result.getArgument());
    						if (maxreps < 0) throw new NumberFormatException();
    					} catch (NumberFormatException nfe){
    						System.err.println(labels.getString("number.bad_argument"));
    						System.exit(0);
    					}
    				} break;
    				case LENGTH:{
    					try {
    						length = Integer.parseInt(result.getArgument());
    						if (length < 0) throw new NumberFormatException();
    					} catch (NumberFormatException nfe){
    						System.err.println(labels.getString("number.bad_argument"));
    						System.exit(0);
    					}
    				} break;
    			}
    		}
    		RandPass randPass = new RandPass();
    		randPass.setAlphabet(alphabet);
    		randPass.setFirstAlphabet(firstAlphabet);
    		randPass.setLastAlphabet(lastAlphabet);
    		randPass.setMaxRepetition(maxreps);
    		for (int i=0; i<reqs.size(); i++){
    			randPass.addRequirement((reqs.elementAt(i)).toCharArray(), 1);
    		}
    		for (int i=0; i<ver.size(); i++){
    			randPass.addVerifier((PasswordVerifier)((Class.forName((ver.elementAt(i)))).newInstance()));
    		}
    		for (int i=0; i<number; i++){
    			System.out.println(randPass.getPass(length));
    		}
    	}
    
    
    	private Vector<Requirement> requirements = null;
    
    	/**
    	 * Require that a certain number of characters from an
    	 * alphabet be present in generated passwords.
    	 *
    	 * @param alphabet set of letters that must be present
    	 * @param num number of letters from the alphabet that must be present.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void addRequirement(char[] alphabet, int num){
    		if (requirements == null) requirements = new Vector<Requirement>();
    		requirements.add(new Requirement(alphabet, num));
    	}
    
    
    	/**
    	 * Set the alphabet used by this random password generator.
    	 *
    	 * @param alphabet Characters allowed in generated passwords.
    	 * @throws NullPointerException if the alphabet is null.
    	 * @throws ArrayIndexOutOfBoundsException if the alphabet has no elements.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void setAlphabet(char[] alphabet){
    		if (alphabet == null) throw new NullPointerException("Null alphabet");
    		if (alphabet.length == 0) throw new ArrayIndexOutOfBoundsException("No characters in alphabet");
    		this.alphabet = alphabet;
    	}
    
    	/**
    	 * Set the random number generator used by this random password generator.
    	 *
    	 * @param rand Secure random number generator to use when generating passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void setRandomGenerator(SecureRandom rand){
    		this.rand = rand;
    	}
    
    	/**
    	 * Set the alphabet used by this random password generator for the first character
    	 * of passwords.
    	 * <p>
    	 * If the alphabet for the first character is set to null or has no elements, the main alphabet will
    	 * be used for the first character.
    	 *
    	 * @param alphabet Characters allowed for the first character of the passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void setFirstAlphabet(char[] alphabet){
    		if (alphabet == null || alphabet.length == 0){
    			this.firstAlphabet = null;
    		} else {
    			this.firstAlphabet = alphabet;
    		}
    	}
    
    	/**
    	 * Set the alphabet used by this random password generator for the last character
    	 * of passwords.
    	 * <p>
    	 * If the alphabet for the last character is set to null or has no elements, the main alphabet will
    	 * be used for the last character.
    	 *
    	 * @param alphabet Characters allowed for the last character of the passwords.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void setLastAlphabet(char[] alphabet){
    		if (alphabet == null || alphabet.length == 0){
    			this.lastAlphabet = null;
    		} else {
    			this.lastAlphabet = alphabet;
    		}
    	}
    
    	/**
    	 * Set the maximum number of characters that may appear in sequence more than
    	 * once in a password.  Your alphabet must be large enough to handle this
    	 * option.  If your alphabet is {'a', 'b'} and you want 8 character passwords
    	 * in which no character appears twice (repetition 1) you are out of luck.
    	 * In such instances your request for no repetition will be ignored.
    	 * <p>
    	 * For example setRepetition(3) will allow a password ababab but not allow
    	 * abcabc.
    	 * <p>
    	 * Using this method can greatly reduce the pool of passwords that are generated.
    	 * For example if only one repetition is allowed then the pool of passwords
    	 * is the permutation of the alphabet rather than the combination.
    	 *
    	 * @param rep Maximum character repetition.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void setMaxRepetition(int rep){
    		this.repetition = rep - 1;
    	}
    
    	/**
    	 * Fill the given buffer with random characters.
    	 * <p>
    	 * Using this method, the password character array can easily
    	 * be reused for efficiency, or overwritten with new random
    	 * characters for security.
    	 * <p>
    	 * NOTE: If it is possible for a hacker to examine memory to find passwords,
    	 * the password should be overwritten in memory as soon as possible after i
    	 * is no longer in use.
    	 *
    	 * @param pass buffer that will hold the password.
    	 * @return the buffer, filled with random characters.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public char[] getPassChars(char[] pass){
    		boolean verified = false;
    		while (!verified){
    			int length = pass.length;
    			for (int i=0; i<length; i++){
    				char[] useAlph = alphabet;
    				if (i == 0 && firstAlphabet != null){
    					useAlph = firstAlphabet;
    				} else if (i == length - 1 && lastAlphabet != null){
    					useAlph = lastAlphabet;
    				}
    				int size = avoidRepetition(useAlph, pass, i, repetition, useAlph.length);
    				pass[i] = useAlph[rand.nextInt(size)];
    			}
    			if (requirements != null) applyRequirements(pass);
    			verified = true;
    			for (int i=0; verified && verifiers != null && i<verifiers.size(); i++){
    				verified = verifiers.elementAt(i).verify(pass);
    			}
    		}
    		return(pass);
    	}
    
    	private Vector<PasswordVerifier> verifiers = null;
    
    	/**
    	 * Add a class that will verify passwords.  No password
    	 * will be returned unless all verifiers approve of it.
    	 *
    	 * @param verifier class that performs verification of password.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public void addVerifier(PasswordVerifier verifier){
    		if (verifiers == null) verifiers = new Vector<PasswordVerifier>();
    		verifiers.add(verifier);
    	}
    
    	private boolean[] touched = null;
    	private int[] available = null;
    
    	private void applyRequirements(char[] pass){
    		int size = requirements.size();
    		if (size > 0){
    			int length = pass.length;
    			if (touched == null || touched.length < length) touched = new boolean[length];
    			if (available == null || available.length < length) available = new int[length];
    			for (int i=0; i<length; i++){
    				touched[i] = false;
    			}
    			for (int reqNum=0; reqNum<size; reqNum++){
    				Requirement req = requirements.elementAt(reqNum);
    				// set the portion of this alphabet available for use.
    				int reqUsedInd = req.alphabet.length;
    				// figure out how much of this requirement is already fulfilled
    				// and what is available to fulfill the rest of it.
    				int fufilledInd = 0;
    				int availableInd = 0;
    				for (int i=0; i<length; i++){
    					if (arrayContains(req.alphabet, pass[i]) && fufilledInd < req.num){
    						fufilledInd++;
    						touched[i] = true;
    						if (repetition >= 0){
    							// move already used characters so they can'
    							// be used again.  This prevents repetition.
    							reqUsedInd -= moveto(req.alphabet, reqUsedInd, pass[i]);
    							// allow repetition if we have no other choice
    							if(reqUsedInd < 0) reqUsedInd = req.alphabet.length;
    						}
    					} else if (!touched[i]){
    						available[availableInd] = i;
    						availableInd++;
    					}
    				}
    				// fulfill the requirement
    				int toDo = req.num - fufilledInd;
    				for (int i=0; i<toDo && availableInd>0; i++){
    					// pick a random available slot
    					// and a random member of the available alphabet
    					int slot = rand.nextInt(availableInd);
    					char passChar = req.alphabet[rand.nextInt(reqUsedInd)];
    					pass[available[slot]] = passChar;
    					touched[available[slot]] = true;
    					// make the slot no longer available
    					availableInd--;
    					available[slot] = available[availableInd];
    					if (repetition >= 0){
    						// move already used characters so they can'
    						// be used again.  This prevents repetition.
    						reqUsedInd -= moveto(req.alphabet, reqUsedInd, passChar);
    						// allow repetition if we have no other choice
    						if(reqUsedInd < 0) reqUsedInd = req.alphabet.length;
    					}
    				}
    			}
    		}
    	}
    
    	private static boolean arrayContains(char[] alph, char c){
    		for (int i=0; i<alph.length; i++){
    			if (alph[i] == c) return true;
    		}
    		return false;
    	}
    
    	/**
    	 * Avoid repetition (if possible) by moving all characters that would cause repetition to
    	 * the end of the alphabet and returning the size of the alphabet that may be used.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	private static int avoidRepetition(char[] alph, char[] pass, int passSize, int repetition, int alphSize){
    		if (repetition > -1){
    			// limit the alphabet to those characters that
    			// will not cause repeating sequences
    			int repPos = 0;
    			while ((repPos = findRep(pass, repPos, passSize, repetition)) != -1){
    				// shuffle characters that would cause repetition
    				// to the end of the alphabet and adjust the size
    				// so that they will not be used.
    				alphSize -= moveto(alph, alphSize, pass[repPos+repetition]);
    				repPos++;
    			}
    			if (alphSize == 0) alphSize = alph.length;
    		}
    		return alphSize;
    	}
    
    	/**
    	 * Find a repetition of the desired length.  The characters to search
    	 * for are located at pass[end-length] to pass[end]
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	private static int findRep(char[] pass, int start, int end, int length){
    		for (int i=start; i<end-length; i++){
    			boolean onTrack = true;
    			for (int j=0; onTrack && j<length; j++){
    				if (pass[i+j] != pass[end-length+j]) onTrack = false;
    			}
    			if(onTrack) return i;
    		}
    		return -1;
    	}
    
    	/**
    	 * move all of the given character to the end of the array
    	 * and return the number of characters moved.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	private static int moveto(char[] alph, int numGood, char c){
    		int count = 0;
    		for (int i=0; i<numGood; i++){
    			if (alph[i] == c){
    				numGood--;
    				char temp = alph[numGood];
    				alph[numGood] = alph[i];
    				alph[i] = temp;
    				count++;
    			}
    		}
    		return count;
    	}
    
    	/**
    	 * Generate a random password of the given length.
    	 * <p>
    	 * NOTE: If it is possible for a hacker to examine memory to find passwords,
    	 * the password should be overwritten in memory as soon as possible after i
    	 * is no longer in use.
    	 *
    	 * @param length The desired length of the generated password.
    	 * @return a random password
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public char[] getPassChars(int length){
    		return(getPassChars(new char[length]));
    	}
    
    	/**
    	 * Generate a random password of the default length (8).
    	 * <p>
    	 * NOTE: If it is possible for a hacker to examine memory to find passwords,
    	 * the password should be overwritten in memory as soon as possible after i
    	 * is no longer in use.
    	 *
    	 * @return a random password
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public char[] getPassChars(){
    		return(getPassChars(DEFAULT_PASSWORD_LENGTH));
    	}
    
    	/**
    	 * Generate a random password of the given length.
    	 * <p>
    	 * NOTE: Strings can not be modified.  If it is possible
    	 * for a hacker to examine memory to find passwords, getPassChars()
    	 * should be used so that the password can be zeroed out of memory
    	 * when no longer in use.
    	 *
    	 * @param length The desired length of the generated password.
    	 * @return a random password
    	 *
    	 * @see #getPassChars(int)
    	 * @since ostermillerutils 1.00.00
    	 */
    	public String getPass(int length){
    		return(new String(getPassChars(new char[length])));
    	}
    
    	/**
    	 * Generate a random password of the default length (8).
    	 * <p>
    	 * NOTE: Strings can not be modified.  If it is possible
    	 * for a hacker to examine memory to find passwords, getPassChars()
    	 * should be used so that the password can be zeroed out of memory
    	 * when no longer in use.
    	 *
    	 * @return a random password
    	 *
    	 * @see #getPassChars()
    	 * @since ostermillerutils 1.00.00
    	 */
    	public String getPass(){
    		return(getPass(DEFAULT_PASSWORD_LENGTH));
    	}
    }