001: /*
002: * Generate random passwords.
003: * Copyright (C) 2001-2007 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * See COPYING.TXT for details.
017: */
018:
019: package com.Ostermiller.util;
020:
021: import java.security.SecureRandom;
022: import java.text.MessageFormat;
023: import java.util.ResourceBundle;
024: import java.util.Locale;
025: import java.util.Vector;
026:
027: /**
028: * Generates a random String using a cryptographically
029: * secure random number generator.
030: * <p>
031: * The alphabet (characters used in the passwords generated)
032: * may be specified, and the random number generator can be
033: * externally supplied.
034: * <p>
035: * Care should be taken when using methods that limit the types
036: * of passwords may be generated. Using an alphabet that is too
037: * small, using passwords that are too short, requiring too many
038: * of a certain type of character, or not allowing repetition,
039: * may decrease security.
040: * <p>
041: * More information about this class is available from <a target="_top" href=
042: * "http://ostermiller.org/utils/RandPass.html">ostermiller.org</a>.
043: *
044: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
045: * @since ostermillerutils 1.00.00
046: */
047: public class RandPass {
048:
049: /**
050: * Version number of this program
051: *
052: * @since ostermillerutils 1.00.00
053: */
054: public static final String version = "1.1";
055:
056: /**
057: * Locale specific strings displayed to the user.
058: *
059: * @since ostermillerutils 1.00.00
060: */
061: protected static ResourceBundle labels = ResourceBundle.getBundle(
062: "com.Ostermiller.util.RandPass", Locale.getDefault());
063:
064: /**
065: * Default length for passwords
066: *
067: * @since ostermillerutils 1.00.00
068: */
069: private static final int DEFAULT_PASSWORD_LENGTH = 8;
070:
071: /**
072: * Alphabet consisting of upper and lower case letters A-Z and
073: * the digits 0-9.
074: *
075: * @since ostermillerutils 1.00.00
076: */
077: public static final char[] NUMBERS_AND_LETTERS_ALPHABET = { 'A',
078: 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
079: 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
080: 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
081: 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
082: 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
083: '9', };
084:
085: /**
086: * Alphabet consisting of all the printable ASCII symbols.
087: *
088: * @since ostermillerutils 1.00.00
089: */
090: public static final char[] SYMBOLS_ALPHABET = { '!', '\"', '#',
091: '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
092: '/', ':', ';', '<', '?', '@', '[', '\\', ']', '^', '_',
093: '`', '{', '|', '}', '~', };
094:
095: /**
096: * Alphabet consisting of all the printable ASCII characters.
097: *
098: * @since ostermillerutils 1.00.00
099: */
100: public static final char[] PRINTABLE_ALPHABET = { '!', '\"', '#',
101: '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
102: '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':',
103: ';', '<', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
104: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
105: 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
106: '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
107: 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
108: 'x', 'y', 'z', '{', '|', '}', '~', };
109:
110: /**
111: * Alphabet consisting of the lower case letters A-Z.
112: *
113: * @since ostermillerutils 1.00.00
114: */
115: public static final char[] LOWERCASE_LETTERS_ALPHABET = { 'a', 'b',
116: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
117: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', };
118:
119: /**
120: * Alphabet consisting of the lower case letters A-Z and
121: * the digits 0-9.
122: *
123: * @since ostermillerutils 1.00.00
124: */
125: public static final char[] LOWERCASE_LETTERS_AND_NUMBERS_ALPHABET = {
126: 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
127: 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
128: 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };
129:
130: /**
131: * Alphabet consisting of upper and lower case letters A-Z.
132: *
133: * @since ostermillerutils 1.00.00
134: */
135: public static final char[] LETTERS_ALPHABET = { 'A', 'B', 'C', 'D',
136: 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
137: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
138: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
139: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', };
140:
141: /**
142: * Alphabet consisting of the upper letters A-Z.
143: *
144: * @since ostermillerutils 1.00.00
145: */
146: public static final char[] UPPERCASE_LETTERS_ALPHABET = { 'A', 'B',
147: 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
148: 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', };
149:
150: /**
151: * Alphabet consisting of upper and lower case letters A-Z and
152: * the digits 0-9 but with characters that are often mistaken
153: * for each other when typed removed. (I,L,O,U,V,i,l,o,u,v,0,1)
154: *
155: * @since ostermillerutils 1.00.00
156: */
157: public static final char[] NONCONFUSING_ALPHABET = { 'A', 'B', 'C',
158: 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R',
159: 'S', 'T', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
160: 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'w',
161: 'x', 'y', 'z', '2', '3', '4', '5', '6', '7', '8', '9', };
162:
163: /**
164: * Random number generator used.
165: *
166: * @since ostermillerutils 1.00.00
167: */
168: protected SecureRandom rand;
169:
170: /**
171: * One less than the maximum number of repeated characters
172: * that are allowed in a password.
173: * Set to -1 to disable this feature.
174: *
175: * @since ostermillerutils 1.00.00
176: */
177: protected int repetition = -1;
178:
179: /**
180: * Set of characters which may be
181: * used in the generated passwords.
182: * <p>
183: * This value may not be null or have
184: * no elements.
185: *
186: * @since ostermillerutils 1.00.00
187: */
188: protected char[] alphabet;
189:
190: /**
191: * Set of characters which may be
192: * used for the first character
193: * in the generated passwords.
194: * <p>
195: * This value may be null but it must
196: * have at least one element otherwise.
197: *
198: * @since ostermillerutils 1.00.00
199: */
200: protected char[] firstAlphabet;
201:
202: /**
203: * Set of characters which may be
204: * used for the last character
205: * in the generated passwords.
206: * <p>
207: * This value may be null but it must
208: * have at least one element otherwise.
209: *
210: * @since ostermillerutils 1.00.00
211: */
212: protected char[] lastAlphabet;
213:
214: /**
215: * Create a new random password generator
216: * with the default secure random number generator
217: * and default NONCONFUSING alphabet for all characters.
218: *
219: * @since ostermillerutils 1.00.00
220: */
221: public RandPass() {
222: this (new SecureRandom(), NONCONFUSING_ALPHABET);
223: }
224:
225: /**
226: * Create a new random password generator
227: * with the given secure random number generator
228: * and default NONCONFUSING alphabet for all characters.
229: *
230: * @param rand Secure random number generator to use when generating passwords.
231: *
232: * @since ostermillerutils 1.00.00
233: */
234: public RandPass(SecureRandom rand) {
235: this (rand, NONCONFUSING_ALPHABET);
236: }
237:
238: /**
239: * Create a new random password generator
240: * with the default secure random number generator
241: * and given alphabet for all characters.
242: *
243: * @param alphabet Characters allowed in generated passwords.
244: *
245: * @since ostermillerutils 1.00.00
246: */
247: public RandPass(char[] alphabet) {
248: this (new SecureRandom(), alphabet);
249: }
250:
251: /**
252: * Create a new random password generator
253: * with the given secure random number generator
254: * and given alphabet for all characters.
255: *
256: * @param rand Secure random number generator to use when generating passwords.
257: * @param alphabet Characters allowed in generated passwords.
258: *
259: * @since ostermillerutils 1.00.00
260: */
261: public RandPass(SecureRandom rand, char[] alphabet) {
262: this .rand = rand;
263: this .alphabet = alphabet;
264: }
265:
266: private class Requirement {
267: private Requirement(char[] alphabet, int num) {
268: this .alphabet = alphabet;
269: this .num = num;
270: }
271:
272: private char[] alphabet;
273: private int num;
274: }
275:
276: private enum RandPassCmdLnOption {
277: /** --help */
278: HELP(new CmdLnOption(labels.getString("help.option"))
279: .setDescription(labels.getString("help.message"))),
280: /** --version */
281: VERSION(new CmdLnOption(labels.getString("version.option"))
282: .setDescription(labels.getString("version.message"))),
283: /** --about */
284: ABOUT(new CmdLnOption(labels.getString("about.option"))
285: .setDescription(labels.getString("about.message"))),
286: /** --alphabet */
287: ALPHABET(new CmdLnOption(labels.getString("alphabet.option"),
288: 'a').setRequiredArgument().setDescription(
289: labels.getString("a.message"))),
290: /** --first */
291: FIRST(new CmdLnOption(
292: labels.getString("first.alphabet.option"), 'F')
293: .setRequiredArgument().setDescription(
294: labels.getString("F.message"))),
295: /** --last */
296: LAST(new CmdLnOption(labels.getString("last.alphabet.option"),
297: 'L').setRequiredArgument().setDescription(
298: labels.getString("L.message"))),
299: /** --number */
300: NUMBER(new CmdLnOption(labels.getString("number.option"), 'n')
301: .setRequiredArgument().setDescription(
302: labels.getString("n.message"))),
303: /** --reps */
304: REPS(new CmdLnOption(labels.getString("maxrep.option"), 'r')
305: .setRequiredArgument().setDescription(
306: labels.getString("r.message"))),
307: /** --length */
308: LENGTH(new CmdLnOption(labels.getString("length.option"), 'l')
309: .setRequiredArgument().setDescription(
310: labels.getString("l.message"))),
311: /** --require */
312: REQUIRE(
313: new CmdLnOption(labels.getString("require.option"), 'R')
314: .setRequiredArgument().setDescription(
315: labels.getString("R.message"))),
316: /** --verify */
317: VERIFY(new CmdLnOption(labels.getString("verify.option"), 'v')
318: .setRequiredArgument().setDescription(
319: labels.getString("v.message")));
320:
321: private CmdLnOption option;
322:
323: private RandPassCmdLnOption(CmdLnOption option) {
324: option.setUserObject(this );
325: this .option = option;
326: }
327:
328: private CmdLnOption getCmdLineOption() {
329: return option;
330: }
331: }
332:
333: /**
334: * Generate a random passwords.
335: * Run with --help argument for more information.
336: *
337: * @param args Command line arguments.
338: * @throws Exception errors
339: *
340: * @since ostermillerutils 1.00.00
341: */
342: public static void main(String[] args) throws Exception {
343: CmdLn commandLine = new CmdLn(args).setDescription(labels
344: .getString("randpass")
345: + labels.getString("purpose.message"));
346: for (RandPassCmdLnOption option : RandPassCmdLnOption.values()) {
347: commandLine.addOption(option.getCmdLineOption());
348: }
349: int number = 1;
350: char[] alphabet = NONCONFUSING_ALPHABET;
351: char[] firstAlphabet = null;
352: char[] lastAlphabet = null;
353: Vector<String> reqs = new Vector<String>();
354: Vector<String> ver = new Vector<String>();
355: int maxreps = 0;
356: int length = 8;
357: for (CmdLnResult result : commandLine.getResults()) {
358: switch ((RandPassCmdLnOption) result.getOption()
359: .getUserObject()) {
360: case HELP: {
361: // print out the help message
362: commandLine.printHelp();
363: System.exit(0);
364: }
365: break;
366: case VERSION: {
367: // print out the version message
368: System.out.println(MessageFormat.format(labels
369: .getString("version"),
370: (Object[]) new String[] { version }));
371: System.exit(0);
372: }
373: break;
374: case ABOUT: {
375: System.out
376: .println(labels.getString("randpass")
377: + " -- "
378: + labels.getString("purpose.message")
379: + "\n"
380: + MessageFormat
381: .format(
382: labels
383: .getString("copyright"),
384: (Object[]) new String[] {
385: "2001-2007",
386: "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)" })
387: + "\n\n" + labels.getString("license"));
388: System.exit(0);
389: }
390: break;
391: case ALPHABET: {
392: String alph = result.getArgument();
393: if (alph.length() == 0) {
394: alphabet = NONCONFUSING_ALPHABET;
395: } else {
396: alphabet = alph.toCharArray();
397: }
398: }
399: break;
400: case FIRST: {
401: String alph = result.getArgument();
402: if (alph.length() == 0) {
403: firstAlphabet = null;
404: } else {
405: firstAlphabet = alph.toCharArray();
406: }
407: }
408: break;
409: case LAST: {
410: String alph = result.getArgument();
411: if (alph.length() == 0) {
412: lastAlphabet = null;
413: } else {
414: lastAlphabet = alph.toCharArray();
415: }
416: }
417: break;
418: case REQUIRE: {
419: String alph = result.getArgument();
420: if (alph.length() != 0) {
421: reqs.add(alph);
422: }
423: }
424: break;
425: case VERIFY: {
426: ver.add(result.getArgument());
427: }
428: break;
429: case NUMBER: {
430: try {
431: number = Integer.parseInt(result.getArgument());
432: if (number <= 0)
433: throw new NumberFormatException();
434: } catch (NumberFormatException nfe) {
435: System.err.println(labels
436: .getString("number.bad_argument"));
437: System.exit(0);
438: }
439: }
440: break;
441: case REPS: {
442: try {
443: maxreps = Integer.parseInt(result.getArgument());
444: if (maxreps < 0)
445: throw new NumberFormatException();
446: } catch (NumberFormatException nfe) {
447: System.err.println(labels
448: .getString("number.bad_argument"));
449: System.exit(0);
450: }
451: }
452: break;
453: case LENGTH: {
454: try {
455: length = Integer.parseInt(result.getArgument());
456: if (length < 0)
457: throw new NumberFormatException();
458: } catch (NumberFormatException nfe) {
459: System.err.println(labels
460: .getString("number.bad_argument"));
461: System.exit(0);
462: }
463: }
464: break;
465: }
466: }
467: RandPass randPass = new RandPass();
468: randPass.setAlphabet(alphabet);
469: randPass.setFirstAlphabet(firstAlphabet);
470: randPass.setLastAlphabet(lastAlphabet);
471: randPass.setMaxRepetition(maxreps);
472: for (int i = 0; i < reqs.size(); i++) {
473: randPass.addRequirement((reqs.elementAt(i)).toCharArray(),
474: 1);
475: }
476: for (int i = 0; i < ver.size(); i++) {
477: randPass.addVerifier((PasswordVerifier) ((Class
478: .forName((ver.elementAt(i)))).newInstance()));
479: }
480: for (int i = 0; i < number; i++) {
481: System.out.println(randPass.getPass(length));
482: }
483: }
484:
485: private Vector<Requirement> requirements = null;
486:
487: /**
488: * Require that a certain number of characters from an
489: * alphabet be present in generated passwords.
490: *
491: * @param alphabet set of letters that must be present
492: * @param num number of letters from the alphabet that must be present.
493: *
494: * @since ostermillerutils 1.00.00
495: */
496: public void addRequirement(char[] alphabet, int num) {
497: if (requirements == null)
498: requirements = new Vector<Requirement>();
499: requirements.add(new Requirement(alphabet, num));
500: }
501:
502: /**
503: * Set the alphabet used by this random password generator.
504: *
505: * @param alphabet Characters allowed in generated passwords.
506: * @throws NullPointerException if the alphabet is null.
507: * @throws ArrayIndexOutOfBoundsException if the alphabet has no elements.
508: *
509: * @since ostermillerutils 1.00.00
510: */
511: public void setAlphabet(char[] alphabet) {
512: if (alphabet == null)
513: throw new NullPointerException("Null alphabet");
514: if (alphabet.length == 0)
515: throw new ArrayIndexOutOfBoundsException(
516: "No characters in alphabet");
517: this .alphabet = alphabet;
518: }
519:
520: /**
521: * Set the random number generator used by this random password generator.
522: *
523: * @param rand Secure random number generator to use when generating passwords.
524: *
525: * @since ostermillerutils 1.00.00
526: */
527: public void setRandomGenerator(SecureRandom rand) {
528: this .rand = rand;
529: }
530:
531: /**
532: * Set the alphabet used by this random password generator for the first character
533: * of passwords.
534: * <p>
535: * If the alphabet for the first character is set to null or has no elements, the main alphabet will
536: * be used for the first character.
537: *
538: * @param alphabet Characters allowed for the first character of the passwords.
539: *
540: * @since ostermillerutils 1.00.00
541: */
542: public void setFirstAlphabet(char[] alphabet) {
543: if (alphabet == null || alphabet.length == 0) {
544: this .firstAlphabet = null;
545: } else {
546: this .firstAlphabet = alphabet;
547: }
548: }
549:
550: /**
551: * Set the alphabet used by this random password generator for the last character
552: * of passwords.
553: * <p>
554: * If the alphabet for the last character is set to null or has no elements, the main alphabet will
555: * be used for the last character.
556: *
557: * @param alphabet Characters allowed for the last character of the passwords.
558: *
559: * @since ostermillerutils 1.00.00
560: */
561: public void setLastAlphabet(char[] alphabet) {
562: if (alphabet == null || alphabet.length == 0) {
563: this .lastAlphabet = null;
564: } else {
565: this .lastAlphabet = alphabet;
566: }
567: }
568:
569: /**
570: * Set the maximum number of characters that may appear in sequence more than
571: * once in a password. Your alphabet must be large enough to handle this
572: * option. If your alphabet is {'a', 'b'} and you want 8 character passwords
573: * in which no character appears twice (repetition 1) you are out of luck.
574: * In such instances your request for no repetition will be ignored.
575: * <p>
576: * For example setRepetition(3) will allow a password ababab but not allow
577: * abcabc.
578: * <p>
579: * Using this method can greatly reduce the pool of passwords that are generated.
580: * For example if only one repetition is allowed then the pool of passwords
581: * is the permutation of the alphabet rather than the combination.
582: *
583: * @param rep Maximum character repetition.
584: *
585: * @since ostermillerutils 1.00.00
586: */
587: public void setMaxRepetition(int rep) {
588: this .repetition = rep - 1;
589: }
590:
591: /**
592: * Fill the given buffer with random characters.
593: * <p>
594: * Using this method, the password character array can easily
595: * be reused for efficiency, or overwritten with new random
596: * characters for security.
597: * <p>
598: * NOTE: If it is possible for a hacker to examine memory to find passwords,
599: * the password should be overwritten in memory as soon as possible after i
600: * is no longer in use.
601: *
602: * @param pass buffer that will hold the password.
603: * @return the buffer, filled with random characters.
604: *
605: * @since ostermillerutils 1.00.00
606: */
607: public char[] getPassChars(char[] pass) {
608: boolean verified = false;
609: while (!verified) {
610: int length = pass.length;
611: for (int i = 0; i < length; i++) {
612: char[] useAlph = alphabet;
613: if (i == 0 && firstAlphabet != null) {
614: useAlph = firstAlphabet;
615: } else if (i == length - 1 && lastAlphabet != null) {
616: useAlph = lastAlphabet;
617: }
618: int size = avoidRepetition(useAlph, pass, i,
619: repetition, useAlph.length);
620: pass[i] = useAlph[rand.nextInt(size)];
621: }
622: if (requirements != null)
623: applyRequirements(pass);
624: verified = true;
625: for (int i = 0; verified && verifiers != null
626: && i < verifiers.size(); i++) {
627: verified = verifiers.elementAt(i).verify(pass);
628: }
629: }
630: return (pass);
631: }
632:
633: private Vector<PasswordVerifier> verifiers = null;
634:
635: /**
636: * Add a class that will verify passwords. No password
637: * will be returned unless all verifiers approve of it.
638: *
639: * @param verifier class that performs verification of password.
640: *
641: * @since ostermillerutils 1.00.00
642: */
643: public void addVerifier(PasswordVerifier verifier) {
644: if (verifiers == null)
645: verifiers = new Vector<PasswordVerifier>();
646: verifiers.add(verifier);
647: }
648:
649: private boolean[] touched = null;
650: private int[] available = null;
651:
652: private void applyRequirements(char[] pass) {
653: int size = requirements.size();
654: if (size > 0) {
655: int length = pass.length;
656: if (touched == null || touched.length < length)
657: touched = new boolean[length];
658: if (available == null || available.length < length)
659: available = new int[length];
660: for (int i = 0; i < length; i++) {
661: touched[i] = false;
662: }
663: for (int reqNum = 0; reqNum < size; reqNum++) {
664: Requirement req = requirements.elementAt(reqNum);
665: // set the portion of this alphabet available for use.
666: int reqUsedInd = req.alphabet.length;
667: // figure out how much of this requirement is already fulfilled
668: // and what is available to fulfill the rest of it.
669: int fufilledInd = 0;
670: int availableInd = 0;
671: for (int i = 0; i < length; i++) {
672: if (arrayContains(req.alphabet, pass[i])
673: && fufilledInd < req.num) {
674: fufilledInd++;
675: touched[i] = true;
676: if (repetition >= 0) {
677: // move already used characters so they can'
678: // be used again. This prevents repetition.
679: reqUsedInd -= moveto(req.alphabet,
680: reqUsedInd, pass[i]);
681: // allow repetition if we have no other choice
682: if (reqUsedInd < 0)
683: reqUsedInd = req.alphabet.length;
684: }
685: } else if (!touched[i]) {
686: available[availableInd] = i;
687: availableInd++;
688: }
689: }
690: // fulfill the requirement
691: int toDo = req.num - fufilledInd;
692: for (int i = 0; i < toDo && availableInd > 0; i++) {
693: // pick a random available slot
694: // and a random member of the available alphabet
695: int slot = rand.nextInt(availableInd);
696: char passChar = req.alphabet[rand
697: .nextInt(reqUsedInd)];
698: pass[available[slot]] = passChar;
699: touched[available[slot]] = true;
700: // make the slot no longer available
701: availableInd--;
702: available[slot] = available[availableInd];
703: if (repetition >= 0) {
704: // move already used characters so they can'
705: // be used again. This prevents repetition.
706: reqUsedInd -= moveto(req.alphabet, reqUsedInd,
707: passChar);
708: // allow repetition if we have no other choice
709: if (reqUsedInd < 0)
710: reqUsedInd = req.alphabet.length;
711: }
712: }
713: }
714: }
715: }
716:
717: private static boolean arrayContains(char[] alph, char c) {
718: for (int i = 0; i < alph.length; i++) {
719: if (alph[i] == c)
720: return true;
721: }
722: return false;
723: }
724:
725: /**
726: * Avoid repetition (if possible) by moving all characters that would cause repetition to
727: * the end of the alphabet and returning the size of the alphabet that may be used.
728: *
729: * @since ostermillerutils 1.00.00
730: */
731: private static int avoidRepetition(char[] alph, char[] pass,
732: int passSize, int repetition, int alphSize) {
733: if (repetition > -1) {
734: // limit the alphabet to those characters that
735: // will not cause repeating sequences
736: int repPos = 0;
737: while ((repPos = findRep(pass, repPos, passSize, repetition)) != -1) {
738: // shuffle characters that would cause repetition
739: // to the end of the alphabet and adjust the size
740: // so that they will not be used.
741: alphSize -= moveto(alph, alphSize, pass[repPos
742: + repetition]);
743: repPos++;
744: }
745: if (alphSize == 0)
746: alphSize = alph.length;
747: }
748: return alphSize;
749: }
750:
751: /**
752: * Find a repetition of the desired length. The characters to search
753: * for are located at pass[end-length] to pass[end]
754: *
755: * @since ostermillerutils 1.00.00
756: */
757: private static int findRep(char[] pass, int start, int end,
758: int length) {
759: for (int i = start; i < end - length; i++) {
760: boolean onTrack = true;
761: for (int j = 0; onTrack && j < length; j++) {
762: if (pass[i + j] != pass[end - length + j])
763: onTrack = false;
764: }
765: if (onTrack)
766: return i;
767: }
768: return -1;
769: }
770:
771: /**
772: * move all of the given character to the end of the array
773: * and return the number of characters moved.
774: *
775: * @since ostermillerutils 1.00.00
776: */
777: private static int moveto(char[] alph, int numGood, char c) {
778: int count = 0;
779: for (int i = 0; i < numGood; i++) {
780: if (alph[i] == c) {
781: numGood--;
782: char temp = alph[numGood];
783: alph[numGood] = alph[i];
784: alph[i] = temp;
785: count++;
786: }
787: }
788: return count;
789: }
790:
791: /**
792: * Generate a random password of the given length.
793: * <p>
794: * NOTE: If it is possible for a hacker to examine memory to find passwords,
795: * the password should be overwritten in memory as soon as possible after i
796: * is no longer in use.
797: *
798: * @param length The desired length of the generated password.
799: * @return a random password
800: *
801: * @since ostermillerutils 1.00.00
802: */
803: public char[] getPassChars(int length) {
804: return (getPassChars(new char[length]));
805: }
806:
807: /**
808: * Generate a random password of the default length (8).
809: * <p>
810: * NOTE: If it is possible for a hacker to examine memory to find passwords,
811: * the password should be overwritten in memory as soon as possible after i
812: * is no longer in use.
813: *
814: * @return a random password
815: *
816: * @since ostermillerutils 1.00.00
817: */
818: public char[] getPassChars() {
819: return (getPassChars(DEFAULT_PASSWORD_LENGTH));
820: }
821:
822: /**
823: * Generate a random password of the given length.
824: * <p>
825: * NOTE: Strings can not be modified. If it is possible
826: * for a hacker to examine memory to find passwords, getPassChars()
827: * should be used so that the password can be zeroed out of memory
828: * when no longer in use.
829: *
830: * @param length The desired length of the generated password.
831: * @return a random password
832: *
833: * @see #getPassChars(int)
834: * @since ostermillerutils 1.00.00
835: */
836: public String getPass(int length) {
837: return (new String(getPassChars(new char[length])));
838: }
839:
840: /**
841: * Generate a random password of the default length (8).
842: * <p>
843: * NOTE: Strings can not be modified. If it is possible
844: * for a hacker to examine memory to find passwords, getPassChars()
845: * should be used so that the password can be zeroed out of memory
846: * when no longer in use.
847: *
848: * @return a random password
849: *
850: * @see #getPassChars()
851: * @since ostermillerutils 1.00.00
852: */
853: public String getPass() {
854: return (getPass(DEFAULT_PASSWORD_LENGTH));
855: }
856: }
|