package com.Ostermiller.util; /** * Generate random passwords. * Copyright (C) 2001-2002 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 COPYING.TXT for details. */ import java.security.SecureRandom; import java.util.Locale; import java.util.ResourceBundle; 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 { private class Requirement { public char[] alphabet; public int num; public Requirement(char[] alphabet, int num) { this.alphabet = alphabet; this.num = num; } } /** * 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( "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 lowercase 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 lowercase 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 lowercase 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 lowercase 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 lowercase 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', }; 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; } // /** // * Generate a random passwords. Run with --help argument for more // * information. // * // * @param args // * Command line arguments. // * // * @since ostermillerutils 1.00.00 // */ // public static void main(String[] args) throws Exception { // // create the command line options that we are looking for // LongOpt[] longopts = { // new LongOpt(labels.getString("help.option"), // LongOpt.NO_ARGUMENT, null, 1), // new LongOpt(labels.getString("version.option"), // LongOpt.NO_ARGUMENT, null, 2), // new LongOpt(labels.getString("about.option"), // LongOpt.NO_ARGUMENT, null, 3), // new LongOpt(labels.getString("alphabet.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'a'), // new LongOpt(labels.getString("first.alphabet.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'F'), // new LongOpt(labels.getString("last.alphabet.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'L'), // new LongOpt(labels.getString("number.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'n'), // new LongOpt(labels.getString("maxrep.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'r'), // new LongOpt(labels.getString("length.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'l'), // new LongOpt(labels.getString("require.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'R'), // new LongOpt(labels.getString("verify.option"), // LongOpt.REQUIRED_ARGUMENT, null, 'v'), }; // String oneLetterOptions = "a:n:F:L:r:l:R:v:"; // Getopt opts = new Getopt(labels.getString("randpass"), args, // oneLetterOptions, longopts); // 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; // int c; // while ((c = opts.getopt()) != -1) { // switch (c) { // case 1: { // // print out the help message // String[] helpFlags = new String[] { // "--" + labels.getString("help.option"), // "--" + labels.getString("version.option"), // "--" + labels.getString("about.option"), // "-a --" + labels.getString("alphabet.option") + " " // + labels.getString("alphabet.argument"), // "-n --" + labels.getString("number.option") + " " // + labels.getString("number.argument"), // "-F --" + labels.getString("first.alphabet.option") // + " " + labels.getString("alphabet.argument"), // "-L --" + labels.getString("last.alphabet.option") // + " " + labels.getString("alphabet.argument"), // "-l --" + labels.getString("length.option") + " " // + labels.getString("number.argument"), // "-r --" + labels.getString("maxrep.option") + " " // + labels.getString("number.argument"), // "-R --" + labels.getString("require.option") + " " // + labels.getString("alphabet.argument"), // "-v --" + labels.getString("verify.option") + " " // + labels.getString("class.argument"), }; // int maxLength = 0; // for (int i = 0; i < helpFlags.length; i++) { // maxLength = Math.max(maxLength, helpFlags[i].length()); // } // maxLength += 2; // System.out.println(labels.getString("randpass") + " [-" // + StringHelper.replace(oneLetterOptions, ":", "") // + "]\n" + labels.getString("purpose.message") + "\n" // + " " // + StringHelper.postpad(helpFlags[0], maxLength, ' ') // + labels.getString("help.message") + "\n" + " " // + StringHelper.postpad(helpFlags[1], maxLength, ' ') // + labels.getString("version.message") + "\n" + " " // + StringHelper.postpad(helpFlags[2], maxLength, ' ') // + labels.getString("about.message") + "\n" + " " // + StringHelper.postpad(helpFlags[3], maxLength, ' ') // + labels.getString("a.message") + "\n" + " " // + StringHelper.postpad(helpFlags[4], maxLength, ' ') // + labels.getString("n.message") + "\n" + " " // + StringHelper.postpad(helpFlags[5], maxLength, ' ') // + labels.getString("F.message") + "\n" + " " // + StringHelper.postpad(helpFlags[6], maxLength, ' ') // + labels.getString("L.message") + "\n" + " " // + StringHelper.postpad(helpFlags[7], maxLength, ' ') // + labels.getString("l.message") + "\n" + " " // + StringHelper.postpad(helpFlags[8], maxLength, ' ') // + labels.getString("r.message") + "\n" + " " // + StringHelper.postpad(helpFlags[9], maxLength, ' ') // + labels.getString("R.message") + "\n" + " " // + StringHelper.postpad(helpFlags[10], maxLength, ' ') // + labels.getString("v.message") + "\n"); // System.exit(0); // } // break; // case 2: { // // print out the version message // System.out.println(MessageFormat.format(labels // .getString("version"), // (Object[]) new String[] { version })); // System.exit(0); // } // break; // case 3: { // System.out // .println(labels.getString("randpass") // + " -- " // + labels.getString("purpose.message") // + "\n" // + MessageFormat // .format( // labels.getString("copyright"), // (Object[]) new String[] { // "2001-2002", // "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)" // }) // + "\n\n" + labels.getString("license")); // System.exit(0); // } // break; // case 'a': { // String alph = opts.getOptarg(); // if (alph.length() == 0) { // alphabet = NONCONFUSING_ALPHABET; // } else { // alphabet = alph.toCharArray(); // } // } // break; // case 'F': { // String alph = opts.getOptarg(); // if (alph.length() == 0) { // firstAlphabet = null; // } else { // firstAlphabet = alph.toCharArray(); // } // } // break; // case 'L': { // String alph = opts.getOptarg(); // if (alph.length() == 0) { // lastAlphabet = null; // } else { // lastAlphabet = alph.toCharArray(); // } // } // break; // case 'R': { // String alph = opts.getOptarg(); // if (alph.length() != 0) { // reqs.add(alph); // } // } // break; // case 'v': { // ver.add(opts.getOptarg()); // } // break; // case 'n': { // try { // number = Integer.parseInt(opts.getOptarg()); // if (number <= 0) // throw new NumberFormatException(); // } catch (NumberFormatException nfe) { // System.err.println(labels.getString("number.bad_argument")); // System.exit(0); // } // } // break; // case 'r': { // try { // maxreps = Integer.parseInt(opts.getOptarg()); // if (maxreps < 0) // throw new NumberFormatException(); // } catch (NumberFormatException nfe) { // System.err.println(labels.getString("number.bad_argument")); // System.exit(0); // } // } // break; // case 'l': { // try { // length = Integer.parseInt(opts.getOptarg()); // if (length < 0) // throw new NumberFormatException(); // } catch (NumberFormatException nfe) { // System.err.println(labels.getString("number.bad_argument")); // System.exit(0); // } // } // break; // default: { // System.exit(0); // } // } // } // 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(((String) (reqs.elementAt(i))) // .toCharArray(), 1); // } // for (int i = 0; i < ver.size(); i++) { // randPass.addVerifier((PasswordVerifier) ((Class // .forName((String) (ver.elementAt(i)))).newInstance())); // } // for (int i = 0; i < number; i++) { // System.out.println(randPass.getPass(length)); // } // } /** * 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; } /** * 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 mus 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 mus have at least one element otherwise. * * @since ostermillerutils 1.00.00 */ protected char[] lastAlphabet; private Vector<Requirement> requirements = null; private Vector<PasswordVerifier> verifiers = null; private boolean[] touched = null; private int[] available = null; /** * 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 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 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 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; } /** * 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)); } /** * 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 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 = (Requirement) 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; } } } } } /** * 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)); } /** * 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: 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)); } /** * 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 = ((PasswordVerifier) verifiers.elementAt(i)) .verify(pass); } } return (pass); } /** * 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])); } /** * 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 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; } /** * 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; } }