KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > unixauth > MD5Crypt


1 /*
2
3  Based on the work of Jonathan Abbey
4  Adopted for Director by Thomas Aeby, 2004
5
6  MD5Crypt.java
7
8  Created: 3 November 1999
9
10  Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu
11  Original C Version:
12  ----------------------------------------------------------------------------
13  "THE BEER-WARE LICENSE" (Revision 42):
14  <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
15  can do whatever you want with this stuff. If we meet some day, and you think
16  this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
17  ----------------------------------------------------------------------------
18
19  -----------------------------------------------------------------------
20  
21  Ganymede Directory Management System
22
23  Copyright (C) 1996, 1997, 1998, 1999 The University of Texas at Austin.
24
25  Contact information
26
27  Author Email: ganymede_author@arlut.utexas.edu
28  Email mailing list: ganymede@arlut.utexas.edu
29
30  US Mail:
31
32  Computer Science Division
33  Applied Research Laboratories
34  The University of Texas at Austin
35  PO Box 8029, Austin TX 78713-8029
36
37  Telephone: (512) 835-3200
38
39  This program is free software; you can redistribute it and/or modify
40  it under the terms of the GNU General Public License as published by
41  the Free Software Foundation; either version 2 of the License, or
42  (at your option) any later version.
43
44  This program is distributed in the hope that it will be useful,
45  but WITHOUT ANY WARRANTY; without even the implied warranty of
46  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47  GNU General Public License for more details.
48
49  You should have received a copy of the GNU General Public License
50  along with this program; if not, write to the Free Software
51  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
52
53  */

54
55 package com.sslexplorer.unixauth;
56
57 import com.maverick.crypto.digests.MD5Digest;
58
59
60 /*------------------------------------------------------------------------------
61  class
62  MD5Crypt
63
64  ------------------------------------------------------------------------------*/

65
66 /**
67  * <p>
68  * This class defines a method,
69  * {@link MD5Crypt#crypt(java.lang.String, java.lang.String) crypt()}, which
70  * takes a password and a salt string and generates an
71  * OpenBSD/FreeBSD/Linux-compatible md5-encoded password entry.
72  * </p>
73  *
74  * <p>
75  * Created: 3 November 1999
76  * </p>
77  * <p>
78  * Release: $Name: SSLEXPLORER_1_0_0_BRANCH $
79  * </p>
80  * <p>
81  * Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu
82  * </p>
83  * <p>
84  * Original C Version:
85  *
86  * <pre>
87  *
88  * ----------------------------------------------------------------------------
89  * &quot;THE BEER-WARE LICENSE&quot; (Revision 42):
90  * &lt;phk@login.dknet.dk&gt; wrote this file. As long as you retain this notice you
91  * can do whatever you want with this stuff. If we meet some day, and you think
92  * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
93  * ----------------------------------------------------------------------------
94  *
95  * </pre>
96  *
97  * </p>
98  */

99
100 public final class MD5Crypt {
101
102     /**
103      *
104      * Command line test rig.
105      *
106      */

107
108     static public void main(String JavaDoc argv[]) {
109         if ((argv.length < 1) || (argv.length > 2)) {
110             System.err.println("Usage: MD5Crypt password salt");
111             System.exit(1);
112         }
113
114         if (argv.length == 2) {
115             System.err.println(MD5Crypt.crypt(argv[0], argv[1]));
116         } else {
117             System.err.println(MD5Crypt.crypt(argv[0]));
118         }
119
120         System.exit(0);
121     }
122
123     static private final String JavaDoc SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
124
125     static private final String JavaDoc itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
126
127     static private final String JavaDoc to64(long v, int size) {
128         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
129
130         while (--size >= 0) {
131             result.append(itoa64.charAt((int) (v & 0x3f)));
132             v >>>= 6;
133         }
134
135         return result.toString();
136     }
137
138     static private final void clearbits(byte bits[]) {
139         for (int i = 0; i < bits.length; i++) {
140             bits[i] = 0;
141         }
142     }
143
144     /**
145      * convert an encoded unsigned byte value into a int with the unsigned
146      * value.
147      */

148
149     static private final int bytes2u(byte inp) {
150         return (int) inp & 0xff;
151     }
152
153     /**
154      * <p>
155      * This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible
156      * md5-encoded password hash from a plaintext password and a salt.
157      * </p>
158      *
159      * <p>
160      * The resulting string will be in the form '$1$&lt;salt&gt;$&lt;hashed
161      * mess&gt;
162      * </p>
163      *
164      * @param password
165      * Plaintext password
166      *
167      * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password field.
168      */

169
170     static public final String JavaDoc crypt(String JavaDoc password) {
171         StringBuffer JavaDoc salt = new StringBuffer JavaDoc();
172         java.util.Random JavaDoc randgen = new java.util.Random JavaDoc();
173
174         /* -- */
175
176         while (salt.length() < 8) {
177             int index = (int) (randgen.nextFloat() * SALTCHARS.length());
178             salt.append(SALTCHARS.substring(index, index + 1));
179         }
180
181         return MD5Crypt.crypt(password, salt.toString());
182     }
183
184     /**
185      * <p>
186      * This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible
187      * md5-encoded password hash from a plaintext password and a salt.
188      * </p>
189      *
190      * <p>
191      * The resulting string will be in the form '$1$&lt;salt&gt;$&lt;hashed
192      * mess&gt;
193      * </p>
194      *
195      * @param password
196      * Plaintext password
197      * @param salt
198      * A short string to use to randomize md5. May start with $1$,
199      * which will be ignored. It is explicitly permitted to pass a
200      * pre-existing MD5Crypt'ed password entry as the salt. crypt()
201      * will strip the salt chars out properly.
202      *
203      * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password field.
204      */

205
206     static public final String JavaDoc crypt(String JavaDoc password, String JavaDoc salt) {
207         /*
208          * This string is magic for this algorithm. Having it this way, we can
209          * get get better later on
210          */

211
212         String JavaDoc magic = "$1$";
213         byte finalState[];
214         MD5Digest ctx, ctx1;
215         long l;
216
217         /* -- */
218
219         /* Refine the Salt first */
220
221         /* If it starts with the magic string, then skip that */
222
223         if (salt.startsWith(magic)) {
224             salt = salt.substring(magic.length());
225         }
226
227         /* It stops at the first '$', max 8 chars */
228
229         if (salt.indexOf('$') != -1) {
230             salt = salt.substring(0, salt.indexOf('$'));
231         }
232
233         if (salt.length() > 8) {
234             salt = salt.substring(0, 8);
235         }
236
237         ctx = new MD5Digest();
238
239         write(ctx, password.getBytes()); // The password first, since that is
240
// what is most unknown
241
write(ctx, magic.getBytes()); // Then our magic string
242
write(ctx, salt.getBytes()); // Then the raw salt
243

244         /* Then just as many characters of the MD5(pw,salt,pw) */
245
246         ctx1 = new MD5Digest();
247         write(ctx1, password.getBytes());
248         write(ctx1, salt.getBytes());
249         write(ctx1, password.getBytes());
250
251
252         finalState = new byte[ctx1.getDigestSize()];
253         ctx1.doFinal(finalState, 0);
254
255         for (int pl = password.length(); pl > 0; pl -= 16) {
256             for (int i = 0; i < (pl > 16 ? 16 : pl); i++)
257                 ctx.update(finalState[i]);
258         }
259
260         /*
261          * the original code claimed that finalState was being cleared to keep
262          * dangerous bits out of memory, but doing this is also required in
263          * order to get the right output.
264          */

265
266         clearbits(finalState);
267
268         /* Then something really weird... */
269
270         for (int i = password.length(); i != 0; i >>>= 1) {
271             if ((i & 1) != 0) {
272                 ctx.update(finalState[0]);
273             } else {
274                 ctx.update(password.getBytes()[0]);
275             }
276         }
277
278         finalState = new byte[ctx.getDigestSize()];
279         ctx.doFinal(finalState, 0);
280
281         /*
282          * and now, just to make sure things don't run too fast On a 60 Mhz
283          * Pentium this takes 34 msec, so you would need 30 seconds to build a
284          * 1000 entry dictionary...
285          *
286          * (The above timings from the C version)
287          */

288
289         for (int i = 0; i < 1000; i++) {
290             ctx1 = new MD5Digest();
291
292             if ((i & 1) != 0) {
293                 write(ctx1, password.getBytes());
294             } else {
295                 for (int c = 0; c < 16; c++)
296                     ctx1.update(finalState[c]);
297             }
298
299             if ((i % 3) != 0) {
300                 write(ctx1, salt.getBytes());
301             }
302
303             if ((i % 7) != 0) {
304                 write(ctx1, password.getBytes());
305             }
306
307             if ((i & 1) != 0) {
308                 for (int c = 0; c < 16; c++)
309                     ctx1.update(finalState[c]);
310             } else {
311                 write(ctx1, password.getBytes());
312             }
313
314
315             finalState = new byte[ctx.getDigestSize()];
316             ctx1.doFinal(finalState, 0);
317         }
318
319         /* Now make the output string */
320
321         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
322
323         result.append(magic);
324         result.append(salt);
325         result.append("$");
326
327         l = (bytes2u(finalState[0]) << 16) | (bytes2u(finalState[6]) << 8) | bytes2u(finalState[12]);
328         result.append(to64(l, 4));
329
330         l = (bytes2u(finalState[1]) << 16) | (bytes2u(finalState[7]) << 8) | bytes2u(finalState[13]);
331         result.append(to64(l, 4));
332
333         l = (bytes2u(finalState[2]) << 16) | (bytes2u(finalState[8]) << 8) | bytes2u(finalState[14]);
334         result.append(to64(l, 4));
335
336         l = (bytes2u(finalState[3]) << 16) | (bytes2u(finalState[9]) << 8) | bytes2u(finalState[15]);
337         result.append(to64(l, 4));
338
339         l = (bytes2u(finalState[4]) << 16) | (bytes2u(finalState[10]) << 8) | bytes2u(finalState[5]);
340         result.append(to64(l, 4));
341
342         l = bytes2u(finalState[11]);
343         result.append(to64(l, 2));
344
345         /* Don't leave anything around in vm they could use. */
346         clearbits(finalState);
347
348         return result.toString();
349     }
350     
351     static void write(MD5Digest digest, byte[] arr) {
352         digest.update(arr, 0, arr.length);
353     }
354 }
Popular Tags