001: /*
002: * This file was extracted from Ganymede 1.0.11 (http://tools.arlut.utexas.edu/gash2/).
003: */
004:
005: /*
006:
007: MD5Crypt.java
008:
009: Created: 3 November 1999
010: Release: $Name: $
011: Version: $Revision: 1.1 $
012: Last Mod Date: $Date: 2004/07/12 13:35:20 $
013: Java Port By: Jonathan Abbey, jonabbey@arlut.utexas.edu
014: Original C Version:
015: ----------------------------------------------------------------------------
016: "THE BEER-WARE LICENSE" (Revision 42):
017: <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
018: can do whatever you want with this stuff. If we meet some day, and you think
019: this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
020: ----------------------------------------------------------------------------
021:
022: -----------------------------------------------------------------------
023:
024: Ganymede Directory Management System
025:
026: Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
027: The University of Texas at Austin.
028:
029: Contact information
030:
031: Web site: http://www.arlut.utexas.edu/gash2
032: Author Email: ganymede_author@arlut.utexas.edu
033: Email mailing list: ganymede@arlut.utexas.edu
034:
035: US Mail:
036:
037: Computer Science Division
038: Applied Research Laboratories
039: The University of Texas at Austin
040: PO Box 8029, Austin TX 78713-8029
041:
042: Telephone: (512) 835-3200
043:
044: This program is free software; you can redistribute it and/or modify
045: it under the terms of the GNU General Public License as published by
046: the Free Software Foundation; either version 2 of the License, or
047: (at your option) any later version.
048:
049: This program is distributed in the hope that it will be useful,
050: but WITHOUT ANY WARRANTY; without even the implied warranty of
051: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
052: GNU General Public License for more details.
053:
054: You should have received a copy of the GNU General Public License
055: along with this program; if not, write to the Free Software
056: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
057: 02111-1307, USA
058:
059: */
060:
061: package md5;
062:
063: /*------------------------------------------------------------------------------
064: class
065: MD5Crypt
066:
067: ------------------------------------------------------------------------------*/
068:
069: /**
070: * <p>This class defines a method,
071: * {@link MD5Crypt#crypt(java.lang.String, java.lang.String) crypt()}, which
072: * takes a password and a salt string and generates an OpenBSD/FreeBSD/Linux-compatible
073: * md5-encoded password entry.</p>
074: *
075: * <p>Created: 3 November 1999</p>
076: * <p>Release: $Name: $</p>
077: * <p>Version: $Revision: 1.1 $</p>
078: * <p>Last Mod Date: $Date: 2004/07/12 13:35:20 $</p>
079: * <p>Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu</p>
080: * <p>Original C Version:<pre>
081: * ----------------------------------------------------------------------------
082: * "THE BEER-WARE LICENSE" (Revision 42):
083: * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
084: * can do whatever you want with this stuff. If we meet some day, and you think
085: * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
086: * ----------------------------------------------------------------------------
087: * </pre></p>
088: *
089: * @author Jonathan Abbey <jonabbey at arlut.utexas.edu>
090: */
091:
092: public final class MD5Crypt {
093:
094: /**
095: *
096: * Command line test rig.
097: *
098: */
099:
100: static public void main(String argv[]) {
101: if ((argv.length < 1) || (argv.length > 3)) {
102: System.err
103: .println("Usage: MD5Crypt [-apache] password salt");
104: System.exit(1);
105: }
106:
107: if (argv.length == 3) {
108: System.err.println(MD5Crypt.apacheCrypt(argv[1], argv[2]));
109: } else if (argv.length == 2) {
110: System.err.println(MD5Crypt.crypt(argv[0], argv[1]));
111: } else {
112: System.err.println(MD5Crypt.crypt(argv[0]));
113: }
114:
115: System.exit(0);
116: }
117:
118: static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
119:
120: static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
121:
122: static private final String to64(long v, int size) {
123: StringBuffer result = new StringBuffer();
124:
125: while (--size >= 0) {
126: result.append(itoa64.charAt((int) (v & 0x3f)));
127: v >>>= 6;
128: }
129:
130: return result.toString();
131: }
132:
133: static private final void clearbits(byte bits[]) {
134: for (int i = 0; i < bits.length; i++) {
135: bits[i] = 0;
136: }
137: }
138:
139: /**
140: * convert an encoded unsigned byte value into a int
141: * with the unsigned value.
142: */
143:
144: static private final int bytes2u(byte inp) {
145: return (int) inp & 0xff;
146: }
147:
148: /**
149: * <p>This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible
150: * md5-encoded password hash from a plaintext password and a
151: * salt.</p>
152: *
153: * <p>The resulting string will be in the form '$1$<salt>$<hashed mess></p>
154: *
155: * @param password Plaintext password
156: *
157: * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password field.
158: */
159:
160: static public final String crypt(String password) {
161: StringBuffer salt = new StringBuffer();
162: java.util.Random randgen = new java.util.Random();
163:
164: /* -- */
165:
166: while (salt.length() < 8) {
167: int index = (int) (randgen.nextFloat() * SALTCHARS.length());
168: salt.append(SALTCHARS.substring(index, index + 1));
169: }
170:
171: return MD5Crypt.crypt(password, salt.toString());
172: }
173:
174: /**
175: * <p>This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible
176: * md5-encoded password hash from a plaintext password and a
177: * salt.</p>
178: *
179: * <p>The resulting string will be in the form '$1$<salt>$<hashed mess></p>
180: *
181: * @param password Plaintext password
182: * @param salt A short string to use to randomize md5. May start with $1$, which
183: * will be ignored. It is explicitly permitted to pass a pre-existing
184: * MD5Crypt'ed password entry as the salt. crypt() will strip the salt
185: * chars out properly.
186: *
187: * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password field.
188: */
189:
190: static public final String crypt(String password, String salt) {
191: return MD5Crypt.crypt(password, salt, "$1$");
192: }
193:
194: /**
195: * <p>This method generates an Apache MD5 compatible
196: * md5-encoded password hash from a plaintext password and a
197: * salt.</p>
198: *
199: * <p>The resulting string will be in the form '$apr1$<salt>$<hashed mess></p>
200: *
201: * @param password Plaintext password
202: *
203: * @return An Apache-compatible md5-hashed password string.
204: */
205:
206: static public final String apacheCrypt(String password) {
207: StringBuffer salt = new StringBuffer();
208: java.util.Random randgen = new java.util.Random();
209:
210: /* -- */
211:
212: while (salt.length() < 8) {
213: int index = (int) (randgen.nextFloat() * SALTCHARS.length());
214: salt.append(SALTCHARS.substring(index, index + 1));
215: }
216:
217: return MD5Crypt.apacheCrypt(password, salt.toString());
218: }
219:
220: /**
221: * <p>This method actually generates an Apache MD5 compatible
222: * md5-encoded password hash from a plaintext password and a
223: * salt.</p>
224: *
225: * <p>The resulting string will be in the form '$apr1$<salt>$<hashed mess></p>
226: *
227: * @param password Plaintext password
228: * @param salt A short string to use to randomize md5. May start with $apr1$, which
229: * will be ignored. It is explicitly permitted to pass a pre-existing
230: * MD5Crypt'ed password entry as the salt. crypt() will strip the salt
231: * chars out properly.
232: *
233: * @return An Apache-compatible md5-hashed password string.
234: */
235:
236: static public final String apacheCrypt(String password, String salt) {
237: return MD5Crypt.crypt(password, salt, "$apr1$");
238: }
239:
240: /**
241: * <p>This method actually generates md5-encoded password hash from
242: * a plaintext password, a salt, and a magic string.</p>
243: *
244: * <p>There are two magic strings that make sense to use here.. '$1$' is the
245: * magic string used by the FreeBSD/Linux/OpenBSD MD5Crypt algorithm, and
246: * '$apr1$' is the magic string used by the Apache MD5Crypt algorithm.</p>
247: *
248: * <p>The resulting string will be in the form '<magic><salt>$<hashed mess></p>
249: *
250: * @param password Plaintext password @param salt A short string to
251: * use to randomize md5. May start with the magic string, which
252: * will be ignored. It is explicitly permitted to pass a
253: * pre-existing MD5Crypt'ed password entry as the salt. crypt()
254: * will strip the salt chars out properly.
255: *
256: * @return An md5-hashed password string.
257: */
258:
259: static public final String crypt(String password, String salt,
260: String magic) {
261: /* This string is magic for this algorithm. Having it this way,
262: * we can get get better later on */
263:
264: byte finalState[];
265: MD5 ctx, ctx1;
266: long l;
267:
268: /* -- */
269:
270: /* Refine the Salt first */
271:
272: /* If it starts with the magic string, then skip that */
273:
274: if (salt.startsWith(magic)) {
275: salt = salt.substring(magic.length());
276: }
277:
278: /* It stops at the first '$', max 8 chars */
279:
280: if (salt.indexOf('$') != -1) {
281: salt = salt.substring(0, salt.indexOf('$'));
282: }
283:
284: if (salt.length() > 8) {
285: salt = salt.substring(0, 8);
286: }
287:
288: ctx = new MD5();
289:
290: ctx.Update(password); // The password first, since that is what is most unknown
291: ctx.Update(magic); // Then our magic string
292: ctx.Update(salt); // Then the raw salt
293:
294: /* Then just as many characters of the MD5(pw,salt,pw) */
295:
296: ctx1 = new MD5();
297: ctx1.Update(password);
298: ctx1.Update(salt);
299: ctx1.Update(password);
300: finalState = ctx1.Final();
301:
302: for (int pl = password.length(); pl > 0; pl -= 16) {
303: ctx.Update(finalState, pl > 16 ? 16 : pl);
304: }
305:
306: /* the original code claimed that finalState was being cleared
307: to keep dangerous bits out of memory, but doing this is also
308: required in order to get the right output. */
309:
310: clearbits(finalState);
311:
312: /* Then something really weird... */
313:
314: for (int i = password.length(); i != 0; i >>>= 1) {
315: if ((i & 1) != 0) {
316: ctx.Update(finalState, 1);
317: } else {
318: ctx.Update(password.getBytes(), 1);
319: }
320: }
321:
322: finalState = ctx.Final();
323:
324: /*
325: * and now, just to make sure things don't run too fast
326: * On a 60 Mhz Pentium this takes 34 msec, so you would
327: * need 30 seconds to build a 1000 entry dictionary...
328: *
329: * (The above timings from the C version)
330: */
331:
332: for (int i = 0; i < 1000; i++) {
333: ctx1 = new MD5();
334:
335: if ((i & 1) != 0) {
336: ctx1.Update(password);
337: } else {
338: ctx1.Update(finalState, 16);
339: }
340:
341: if ((i % 3) != 0) {
342: ctx1.Update(salt);
343: }
344:
345: if ((i % 7) != 0) {
346: ctx1.Update(password);
347: }
348:
349: if ((i & 1) != 0) {
350: ctx1.Update(finalState, 16);
351: } else {
352: ctx1.Update(password);
353: }
354:
355: finalState = ctx1.Final();
356: }
357:
358: /* Now make the output string */
359:
360: StringBuffer result = new StringBuffer();
361:
362: result.append(magic);
363: result.append(salt);
364: result.append("$");
365:
366: l = (bytes2u(finalState[0]) << 16)
367: | (bytes2u(finalState[6]) << 8)
368: | bytes2u(finalState[12]);
369: result.append(to64(l, 4));
370:
371: l = (bytes2u(finalState[1]) << 16)
372: | (bytes2u(finalState[7]) << 8)
373: | bytes2u(finalState[13]);
374: result.append(to64(l, 4));
375:
376: l = (bytes2u(finalState[2]) << 16)
377: | (bytes2u(finalState[8]) << 8)
378: | bytes2u(finalState[14]);
379: result.append(to64(l, 4));
380:
381: l = (bytes2u(finalState[3]) << 16)
382: | (bytes2u(finalState[9]) << 8)
383: | bytes2u(finalState[15]);
384: result.append(to64(l, 4));
385:
386: l = (bytes2u(finalState[4]) << 16)
387: | (bytes2u(finalState[10]) << 8)
388: | bytes2u(finalState[5]);
389: result.append(to64(l, 4));
390:
391: l = bytes2u(finalState[11]);
392: result.append(to64(l, 2));
393:
394: /* Don't leave anything around in vm they could use. */
395: clearbits(finalState);
396:
397: return result.toString();
398: }
399: }
|