001: /*
002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
004: */
005: package com.sun.portal.rewriter.util.crypto;
006:
007: import com.sun.portal.rewriter.util.StringHelper;
008:
009: import java.security.SecureRandom;
010: import java.util.Vector;
011:
012: public final class RandomString {
013: private static final char[] NUMBERS_AND_LETTERS_ALPHABET = { 'A',
014: 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
015: 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
016: 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
017: 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
018: 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
019: '9', };
020:
021: private static final char[] NONCONFUSING_ALPHABET = { 'A', 'B',
022: 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
023: 'R', 'S', 'T', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e',
024: 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't',
025: 'w', 'x', 'y', 'z', '2', '3', '4', '5', '6', '7', '8', '9', };
026:
027: private SecureRandom rand;
028:
029: //-1 to see no repetition allowed
030: private int repetition = -1;
031: private char[] alphabet;
032: private char[] firstAlphabet;
033: private char[] lastAlphabet;
034:
035: private RandomString(byte[] aSeedBytes) {
036: this .rand = new SecureRandom(aSeedBytes);
037: this .alphabet = NONCONFUSING_ALPHABET;
038: }//constructor
039:
040: private Vector requirements = null;
041:
042: private void addRequirement(char[] alphabet, int num) {
043: if (requirements == null) {
044: requirements = new Vector();
045: }
046:
047: requirements.add(new Requirement(alphabet, num));
048: }//addRequirement()
049:
050: /**
051: * Set the alphabet used by this random password generator.
052: *
053: * @param alphabet Characters allowed in generated passwords.
054: * @throws NullPointerException if the alphabet is null.
055: * @throws ArrayIndexOutOfBoundsException if the alphabet has no elements.
056: */
057: private void setAlphabet(char[] alphabet) {
058: if (alphabet == null)
059: throw new NullPointerException("Null alphabet");
060: if (alphabet.length == 0)
061: throw new ArrayIndexOutOfBoundsException(
062: "No characters in alphabet");
063: this .alphabet = alphabet;
064: }
065:
066: public void setFirstAlphabet(char[] alphabet) {
067: if (alphabet == null || alphabet.length == 0) {
068: this .firstAlphabet = null;
069: } else {
070: this .firstAlphabet = alphabet;
071: }
072: }
073:
074: /**
075: * Set the alphabet used by this random password generator for the last character
076: * of passwords.
077: * <p>
078: * If the alphabet for the last character is set to null or has no elements, the main alphabet will
079: * be used for the last character.
080: *
081: * @param alphabet Characters allowed for the last character of the passwords.
082: */
083: public void setLastAlphabet(char[] alphabet) {
084: if (alphabet == null || alphabet.length == 0) {
085: this .lastAlphabet = null;
086: } else {
087: this .lastAlphabet = alphabet;
088: }
089: }
090:
091: /**
092: * Set the maximum number of characters that may appear in sequence more than
093: * once in a password. Your alphabet must be large enough to handle this
094: * option. If your alphabet is {'a', 'b'} and you want 8 character passwords
095: * in which no character appears twice (repetition 1) you are out of luck.
096: * In such instances your request for no repetition will be ignored.
097: * <p>
098: * For example setRepetition(3) will allow a password ababab but not allow
099: * abcabc.
100: * <p>
101: * Using this method can greatly reduce the pool of passwords that are generated.
102: * For example if only one repetition is allowed then the pool of passwords
103: * is the permutation of the alphabet rather than the combination.
104: *
105: * @param rep Maximum character repetition.
106: */
107: public void setMaxRepetition(int rep) {
108: this .repetition = rep - 1;
109: }
110:
111: /**
112: * Fill the given buffer with random characters.
113: * <p>
114: * Using this method, the password character array can easily
115: * be reused for efficiency, or overwritten with new random
116: * characters for security.
117: * <p>
118: * NOTE: If it is possible for a hacker to examine memory to find passwords,
119: * the password should be overwritten in memory as soon as possible after i
120: * is no longer in use.
121: *
122: * @param pass buffer that will hold the password.
123: * @return the buffer, filled with random characters.
124: */
125: public char[] getPassChars(char[] pass) {
126: boolean verified = false;
127: while (!verified) {
128: int length = pass.length;
129: for (int i = 0; i < length; i++) {
130: char[] useAlph = alphabet;
131: if (i == 0 && firstAlphabet != null) {
132: useAlph = firstAlphabet;
133: } else if (i == length - 1 && lastAlphabet != null) {
134: useAlph = lastAlphabet;
135: }
136:
137: int size = avoidRepetition(useAlph, pass, i,
138: repetition, useAlph.length);
139: pass[i] = useAlph[rand.nextInt(size)];
140: }
141: if (requirements != null) {
142: applyRequirements(pass);
143: }
144:
145: verified = true;
146: }
147: return (pass);
148: }
149:
150: private boolean[] touched = null;
151: private int[] available = null;
152:
153: private void applyRequirements(char[] pass) {
154: int size = requirements.size();
155: if (size > 0) {
156: int length = pass.length;
157: if (touched == null || touched.length < length) {
158: touched = new boolean[length];
159: }
160:
161: if (available == null || available.length < length) {
162: available = new int[length];
163: }
164:
165: for (int i = 0; i < length; i++) {
166: touched[i] = false;
167: }
168:
169: for (int reqNum = 0; reqNum < size; reqNum++) {
170: Requirement req = (Requirement) requirements
171: .elementAt(reqNum);
172: // set the portion of this alphabet available for use.
173: int reqUsedInd = req.alphabet.length;
174: // figure out how much of this requirement is already fulfilled
175: // and what is available to fulfill the rest of it.
176: int fufilledInd = 0;
177: int availableInd = 0;
178: for (int i = 0; i < length; i++) {
179: if (arrayContains(req.alphabet, pass[i])
180: && fufilledInd < req.num) {
181: fufilledInd++;
182: touched[i] = true;
183: if (repetition >= 0) {
184: // move already used characters so they can'
185: // be used again. This prevents repetition.
186: reqUsedInd -= moveto(req.alphabet,
187: reqUsedInd, pass[i]);
188: // allow repetition if we have no other choice
189: if (reqUsedInd < 0) {
190: reqUsedInd = req.alphabet.length;
191: }
192: }
193: } else if (!touched[i]) {
194: available[availableInd] = i;
195: availableInd++;
196: }
197: }
198: // fulfill the requirement
199: int toDo = req.num - fufilledInd;
200: for (int i = 0; i < toDo && availableInd > 0; i++) {
201: // pick a random available slot
202: // and a random member of the available alphabet
203: int slot = rand.nextInt(availableInd);
204: char passChar = req.alphabet[rand
205: .nextInt(reqUsedInd)];
206: pass[available[slot]] = passChar;
207: touched[available[slot]] = true;
208: // make the slot no longer available
209: availableInd--;
210: available[slot] = available[availableInd];
211: if (repetition >= 0) {
212: // move already used characters so they can'
213: // be used again. This prevents repetition.
214: reqUsedInd -= moveto(req.alphabet, reqUsedInd,
215: passChar);
216: // allow repetition if we have no other choice
217: if (reqUsedInd < 0) {
218: reqUsedInd = req.alphabet.length;
219: }
220: }
221: }
222: }
223: }
224: }
225:
226: private static boolean arrayContains(char[] alph, char c) {
227: for (int i = 0; i < alph.length; i++) {
228: if (alph[i] == c)
229: return true;
230: }
231: return false;
232: }
233:
234: private static int avoidRepetition(char[] alph, char[] pass,
235: int passSize, int repetition, int alphSize) {
236: if (repetition > -1) {
237: // limit the alphabet to those characters that
238: // will not cause repeating sequences
239: int repPos = 0;
240: while ((repPos = findRep(pass, repPos, passSize, repetition)) != -1) {
241: // shuffle characters that would cause repetition
242: // to the end of the alphabet and adjust the size
243: // so that they will not be used.
244: alphSize -= moveto(alph, alphSize, pass[repPos
245: + repetition]);
246: repPos++;
247: }
248:
249: if (alphSize == 0) {
250: alphSize = alph.length;
251: }
252: }
253: return alphSize;
254: }
255:
256: private static int findRep(char[] pass, int start, int end,
257: int length) {
258: for (int i = start; i < end - length; i++) {
259: boolean onTrack = true;
260: for (int j = 0; onTrack && j < length; j++) {
261: if (pass[i + j] != pass[end - length + j])
262: onTrack = false;
263: }
264: if (onTrack)
265: return i;
266: }
267: return -1;
268: }
269:
270: private static int moveto(char[] alph, int numGood, char c) {
271: int count = 0;
272: for (int i = 0; i < numGood; i++) {
273: if (alph[i] == c) {
274: numGood--;
275: char temp = alph[numGood];
276: alph[numGood] = alph[i];
277: alph[i] = temp;
278: count++;
279: }
280: }
281: return count;
282: }//moveto()
283:
284: private String getPass(int length) {
285: return (new String(getPassChars(new char[length])));
286: }//getPass()
287:
288: public static String create(String aSeed) {
289: if (aSeed == null || aSeed.length() < 8) {
290: aSeed = "Use Nagendra" + aSeed + "as Password";
291: }
292:
293: aSeed = StringHelper.searchAndReplace(aSeed, " ", "raja");
294: char[] alphabet = NONCONFUSING_ALPHABET;
295: char[] firstAlphabet = NUMBERS_AND_LETTERS_ALPHABET;
296: char[] lastAlphabet = aSeed.toCharArray();
297: Vector reqs = new Vector();
298: int length = (lastAlphabet.length > 8)
299: || (lastAlphabet.length < 6) ? 8 : lastAlphabet.length;
300: RandomString randPass = new RandomString(aSeed.getBytes());
301: randPass.setAlphabet(alphabet);
302: randPass.setFirstAlphabet(firstAlphabet);
303: randPass.setLastAlphabet(lastAlphabet);
304: for (int i = 0; i < reqs.size(); i++) {
305: randPass.addRequirement(((String) (reqs.elementAt(i)))
306: .toCharArray(), 1);
307: }
308:
309: return randPass.getPass(length);
310: }//create()
311:
312: public static void main(String[] args) throws Exception {
313: System.out
314: .println(create("Nawrerwerwerewrewrewrwewrerwrwrewrewerwrwerwehgdurtxfgdsg"));
315: }//main()
316:
317: private static class Requirement {
318: private char[] alphabet;
319: private int num;
320:
321: public Requirement(char[] alphabet, int num) {
322: this .alphabet = alphabet;
323: this .num = num;
324: }
325: }//Inner class Requirement
326:
327: }//class RandomString
|