001 package com.sptci.util;
002
003 import java.security.SecureRandom;
004 import java.util.logging.Level;
005 import java.util.logging.Logger;
006
007 /**
008 * A utility class that can be used to generate secure random password
009 * values. Also contains methods to check the quality of a password.
010 *
011 * <p>Copyright 2005 Sans Pareil Technologies, Inc.</p>
012 * @author Rakesh Vidyadharan 2005 August 25
013 * @version $Id: PasswordGenerator.java 3145 2007-04-24 14:14:22Z rakesh $
014 */
015 public class PasswordGenerator extends Object
016 {
017 /**
018 * The default characters that are allowed in a password value.
019 */
020 private static final char[] PASSWORD_CHARACTERS =
021 ( "abcdefghijklmnopqrstuvqxyz" +
022 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
023 "`1234567890~!@#$%^&*()" ).toCharArray();
024
025 /**
026 * The random number generator.
027 */
028 private static final SecureRandom random = new SecureRandom();
029
030 /**
031 * Default constructor. No special actions required.
032 */
033 public PasswordGenerator() {}
034
035 /**
036 * Generate a random password of the specified length.
037 *
038 * @see #generate( int, char[] )
039 * @param length The desired length of the password.
040 * @return char[] The array of characters that comprise the random
041 * password.
042 */
043 public char[] generate( int length )
044 {
045 return generate( length, PASSWORD_CHARACTERS );
046 }
047
048 /**
049 * Generate a random password of the specified length and restricted
050 * to the characters specified. Checks the generated value against
051 * {@link #checkSimple} and to ensure that the generated value is good
052 * enough.
053 *
054 * <p><b>Note:</b> There is a possibility that this method can go into
055 * an infinite loop. This can happen if the <code>characters</code>
056 * array contains only those characters that make {@link
057 * #checkComplex} always return <code>false</code>.</p>
058 *
059 * @see #checkComplex
060 * @param length The desired length of the password.
061 * @param characters The array of valid characters thay may be used
062 * in the password.
063 * @return char[] The array of characters that comprise the random
064 * password.
065 */
066 public char[] generate( int length, char[] characters )
067 {
068 char[] password = new char[length];
069
070 try
071 {
072 random.setSeed( System.currentTimeMillis() );
073
074 byte[] intbytes = new byte[4];
075
076 for ( int i = 0; i < length; ++i )
077 {
078 random.nextBytes( intbytes );
079 password[i] = characters[
080 Math.abs( getIntFromByte( intbytes ) % characters.length ) ];
081 }
082
083 while ( ! checkComplex( password ) )
084 {
085 password = generate( length, characters );
086 }
087 }
088 catch ( Throwable t )
089 {
090 Logger.getAnonymousLogger().log( Level.FINE,
091 "Unable to generate random password.", t );
092 }
093
094 return password;
095 }
096
097 /**
098 * Check the specified password value for quality using simple rules.
099 * The following rules are used:
100 *
101 * <ol>
102 * <li>Have at least 6 characters.</li>
103 * <li>Have at least 2 alphabetical characters.</li>
104 * <li>Have at least 2 non-alphabetical characters.</li>
105 * </ol>
106 *
107 * @see #checkComplex
108 * @param password The password that is to be checked.
109 * @return Returns <code>true</code> if the specified password
110 * satisfies the requirements.
111 */
112 public boolean checkSimple( char[] password )
113 {
114 boolean result = true;
115 if ( password.length < 6 )
116 {
117 result = false;
118 }
119 else
120 {
121 int alpha = 0;
122 int nonalpha = 0;
123 for ( char c : password )
124 {
125 if ( Character.isLetter( c ) )
126 {
127 alpha++;
128 }
129 else
130 {
131 nonalpha++;
132 }
133 }
134
135 result = ( ( alpha > 1 ) && ( nonalpha > 1 ) );
136 }
137
138 return result;
139 }
140
141 /**
142 * Check the specified password value for quality. The following
143 * rules are used:
144 *
145 * <ol>
146 * <li>Have at least 6 characters.</li>
147 * <li>Have at least 2 alphabetical characters.</li>
148 * <li>Have at least 1 numeric characters.</li>
149 * <li>Have at least 1 upper case character.</li>
150 * <li>Have at least 1 lower case character.</li>
151 * <li>Have at least 1 non-alphanumeric character.</li>
152 * </ol>
153 *
154 * <p><b>Note:</b> There is no guarantee that the password generated
155 * by the {@link #generate( int, char[] )} method will satisfy
156 * these rules.</p>
157 *
158 * @see #checkSimple
159 * @param password The password that is to be checked.
160 * @return Returns <code>true</code> if the specified password
161 * satisfies the requirements.
162 */
163 public boolean checkComplex( char[] password )
164 {
165 boolean result = true;
166 if ( password.length < 6 )
167 {
168 result = false;
169 }
170 else
171 {
172 int alpha = 0;
173 int digit = 0;
174 int nonalphanum = 0;
175 int lower = 0;
176 int upper = 0;
177 for ( char c : password )
178 {
179 if ( Character.isLetter( c ) )
180 {
181 alpha++;
182 if ( Character.isLowerCase( c ) )
183 {
184 lower++;
185 }
186 else
187 {
188 upper++;
189 }
190 }
191 else if ( Character.isDigit( c ) )
192 {
193 digit++;
194 }
195 else
196 {
197 nonalphanum++;
198 }
199 }
200
201 result = ( ( alpha > 1 ) && ( digit > 0 ) && ( nonalphanum > 0 ) &&
202 ( lower > 0 ) && ( upper > 0 ) );
203 }
204
205 return result;
206 }
207
208 /**
209 * Convert a byte value returned by the random number generator
210 * ({@link #random}) to an integer that may be used to index into
211 * the allowable characters array.
212 *
213 * @param bytes The byte values returned by the random number
214 * generator.
215 */
216 protected int getIntFromByte( byte[] bytes )
217 {
218 int returnNumber = 0;
219 int index = 0;
220 returnNumber += byteToInt( bytes[index++] ) << 24;
221 returnNumber += byteToInt( bytes[index++] ) << 16;
222 returnNumber += byteToInt( bytes[index++] ) << 8;
223 returnNumber += byteToInt( bytes[index++] ) << 0;
224 return returnNumber;
225 }
226
227 /**
228 * Convert a byte value into a postive integer.
229 *
230 * @param byteValue The byte that is to be converted into an integer.
231 * @return int The converted positive value.
232 */
233 protected int byteToInt( byte byteValue )
234 {
235 return (int) byteValue & 0xFF;
236 }
237 }