KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > security > PasswordDigest


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.server.security;
31
32 import com.caucho.config.ConfigException;
33 import com.caucho.util.Base64;
34 import com.caucho.util.CharBuffer;
35 import com.caucho.util.L10N;
36
37 import javax.annotation.PostConstruct;
38 import javax.servlet.ServletContext JavaDoc;
39 import javax.servlet.ServletException JavaDoc;
40 import javax.servlet.http.HttpServletRequest JavaDoc;
41 import javax.servlet.http.HttpServletResponse JavaDoc;
42 import java.security.MessageDigest JavaDoc;
43
44 /**
45  * Calculates a digest for the user and password.
46  *
47  * <p>If the realm is missing, the digest will calculate:
48  * <code><pre>
49  * MD5(user + ':' + password)
50  * </pre></code>
51  *
52  * <p>If the realm is specified, the digest will calculate:
53  * <code><pre>
54  * MD5(user + ':' + realm + ':' + password)
55  * </pre></code>
56  *
57  * <p>The second version matches the way HTTP digest authentication
58  * is handled, so it is the preferred method for storing passwords.
59  *
60  * <p>The returned result is the base64 encoding of the digest.
61  */

62 public class PasswordDigest {
63   private final L10N L = new L10N(PasswordDigest.class);
64   
65   private String JavaDoc _algorithm = "MD5";
66   private String JavaDoc _format = "base64";
67   private String JavaDoc _realm = null;
68   private MessageDigest JavaDoc _digest;
69   private byte[] _digestBytes = new byte[256];
70   private boolean _isOldEncoding;
71   
72   /**
73    * Returns the message digest algorithm.
74    */

75   public void setAlgorithm(String JavaDoc algorithm)
76   {
77     _algorithm = algorithm;
78   }
79   
80   /**
81    * Returns the message digest algorithm.
82    */

83   public String JavaDoc getAlgorithm()
84   {
85     return _algorithm;
86   }
87   
88   /**
89    * Set the message digest format (base64 or hex).
90    */

91   public void setFormat(String JavaDoc format)
92   {
93     _format = format;
94   }
95   
96   /**
97    * Returns the message digest format (base64 or hex).
98    */

99   public String JavaDoc getFormat()
100   {
101     return _format;
102   }
103   
104   /**
105    * Set the message digest default realm
106    */

107   public void setRealm(String JavaDoc realm)
108   {
109     _realm = realm;
110   }
111   
112   /**
113    * Returns the message digest default realm.
114    */

115   public String JavaDoc getRealm()
116   {
117     return _realm;
118   }
119
120   /**
121    * Sets true for the old, buggy encoding.
122    */

123   public void setOldEncoding(boolean isOldEncoding)
124   {
125     _isOldEncoding = isOldEncoding;
126   }
127
128   /**
129    * Sets the algorithm for bean-style init.
130    */

131   public void addText(String JavaDoc value)
132     throws ConfigException
133   {
134     int p = value.indexOf('-');
135
136     if (p > 0) {
137       String JavaDoc algorithm = value.substring(0, p);
138       String JavaDoc format = value.substring(p + 1);
139
140       setAlgorithm(algorithm);
141       setFormat(format);
142     }
143     else if (value.equals("none")) {
144       setAlgorithm(null);
145       setAlgorithm(null);
146     }
147     else
148       throw new ConfigException(L.l("{0} is an illegal algorithm. Expected `none' or `alogorithm-format', for example `MD5-base64'.", value));
149   }
150
151   /**
152    * Initialize the digest.
153    */

154   @PostConstruct
155   synchronized public void init()
156     throws ServletException JavaDoc
157   {
158     if (_algorithm == null)
159       return;
160     
161     try {
162       _digest = MessageDigest.getInstance(_algorithm);
163     } catch (Exception JavaDoc e) {
164       throw new ServletException JavaDoc(e);
165     }
166   }
167
168   /**
169    * Returns the digest of the password
170    */

171   public String JavaDoc getPasswordDigest(String JavaDoc password)
172     throws ServletException JavaDoc
173   {
174     return getPasswordDigest(null, null, null, password, _realm);
175   }
176
177   /**
178    * Returns the digest of the user/password
179    */

180   public String JavaDoc getPasswordDigest(String JavaDoc user, String JavaDoc password)
181     throws ServletException JavaDoc
182   {
183     return getPasswordDigest(null, null, null, user, password, _realm);
184   }
185
186   /**
187    * Returns the digest of the user/password
188    */

189   public String JavaDoc getPasswordDigest(String JavaDoc user, String JavaDoc password, String JavaDoc realm)
190     throws ServletException JavaDoc
191   {
192     return getPasswordDigest(null, null, null, user, password, realm);
193   }
194   
195   /**
196    * Returns the digest of the user/password
197    *
198    * <p>The default implementation returns the digest of
199    * <b>user:password</b> or <b>user:realm:password</b> if a
200    * default realm has been configured.
201    *
202    * @param request the http request
203    * @param response the http response
204    * @param app the servlet context
205    * @param user the user name
206    * @param password the cleartext password
207    */

208   public String JavaDoc getPasswordDigest(HttpServletRequest JavaDoc request,
209                                   HttpServletResponse JavaDoc response,
210                                   ServletContext JavaDoc app,
211                                   String JavaDoc user, String JavaDoc password)
212     throws ServletException JavaDoc
213   {
214     return getPasswordDigest(request, response, app, user, password, _realm);
215   }
216   
217   /**
218    * Returns the digest of the user/password
219    *
220    * <p>The default implementation returns the digest of
221    * <b>user:realm:password</b>. If the realm is null, it will use
222    * <b>user:password</b>.
223    *
224    * @param request the http request
225    * @param response the http response
226    * @param app the servlet context
227    * @param user the user name
228    * @param password the cleartext password
229    * @param realm the security realm
230    */

231   public String JavaDoc getPasswordDigest(HttpServletRequest JavaDoc request,
232                                   HttpServletResponse JavaDoc response,
233                                   ServletContext JavaDoc app,
234                                   String JavaDoc user, String JavaDoc password, String JavaDoc realm)
235     throws ServletException JavaDoc
236   {
237     if (_digest == null)
238       init();
239     
240     try {
241       synchronized (_digest) {
242         _digest.reset();
243
244     updateDigest(_digest, user, password, realm);
245
246         int len = _digest.digest(_digestBytes, 0, _digestBytes.length);
247
248         return digestToString(_digestBytes, len);
249       }
250     } catch (Exception JavaDoc e) {
251       throw new ServletException JavaDoc(e);
252     }
253   }
254
255   /**
256    * Updates the digest based on the user:realm:password
257    */

258   protected void updateDigest(MessageDigest JavaDoc digest,
259                   String JavaDoc user, String JavaDoc password, String JavaDoc realm)
260   {
261     if (user != null) {
262       addDigestUTF8(digest, user);
263       digest.update((byte) ':');
264     }
265     
266     if (realm != null && ! realm.equals("none")) {
267       addDigestUTF8(digest, realm);
268       digest.update((byte) ':');
269     }
270     
271     addDigestUTF8(digest, password);
272   }
273
274   /**
275    * Adds the string to the digest using a UTF8 encoding.
276    */

277   protected static void addDigestUTF8(MessageDigest JavaDoc digest, String JavaDoc string)
278   {
279     if (string == null)
280       return;
281     
282     int len = string.length();
283     for (int i = 0; i < len; i++) {
284       int ch = string.charAt(i);
285       if (ch < 0x80)
286         digest.update((byte) ch);
287       else if (ch < 0x800) {
288         digest.update((byte) (0xc0 + (ch >> 6)));
289         digest.update((byte) (0x80 + (ch & 0x3f)));
290       }
291       else {
292         digest.update((byte) (0xe0 + (ch >> 12)));
293         digest.update((byte) (0x80 + ((ch >> 6) & 0x3f)));
294         digest.update((byte) (0x80 + (ch & 0x3f)));
295       }
296     }
297   }
298
299   /**
300    * Convert the string to a digest byte array.
301    */

302   protected byte[]stringToDigest(String JavaDoc s)
303   {
304     return Base64.decodeToByteArray(s);
305   }
306   
307   /**
308    * Convert the digest byte array to a string.
309    */

310   protected String JavaDoc digestToString(byte []digest, int len)
311   {
312     if (! _format.equals("base64"))
313       return digestToHex(digest, len);
314     else if (_isOldEncoding)
315       return digestToOldBase64(digest, len);
316     else
317       return digestToBase64(digest, len);
318   }
319   
320   protected static String JavaDoc digestToBase64(byte []digest, int len)
321   {
322     CharBuffer cb = CharBuffer.allocate();
323
324     Base64.encode(cb, digest, 0, len);
325
326     return cb.close();
327   }
328   
329   protected static String JavaDoc digestToOldBase64(byte []digest, int len)
330   {
331     CharBuffer cb = CharBuffer.allocate();
332
333     Base64.oldEncode(cb, digest, 0, len);
334
335     return cb.close();
336   }
337   
338   protected static String JavaDoc digestToHex(byte []digest, int len)
339   {
340     CharBuffer cb = CharBuffer.allocate();
341
342     for (int i = 0; i < len; i++) {
343       int d1 = (digest[i] >> 4) & 0xf;
344       int d2 = digest[i] & 0xf;
345
346       if (d1 >= 10)
347         cb.append((char) (d1 + 'a' - 10));
348       else
349         cb.append((char) (d1 + '0'));
350       
351       if (d2 >= 10)
352         cb.append((char) (d2 + 'a' - 10));
353       else
354         cb.append((char) (d2 + '0'));
355     }
356
357     return cb.close();
358   }
359 }
360
Popular Tags