View Javadoc

1   // ========================================================================
2   // Copyright 1998-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty.security;
16  
17  import java.security.MessageDigest;
18  
19  import org.mortbay.log.Log;
20  import org.mortbay.util.StringUtil;
21  import org.mortbay.util.TypeUtil;
22  
23  
24  /* ------------------------------------------------------------ */
25  /** Credentials.
26   * The Credential class represents an abstract mechanism for checking
27   * authentication credentials.  A credential instance either represents a
28   * secret, or some data that could only be derived from knowing the secret.
29   * <p>
30   * Often a Credential is related to a Password via a one way algorithm, so
31   * while a Password itself is a Credential, a UnixCrypt or MD5 digest of a
32   * a password is only a credential that can be checked against the password.
33   * <p>
34   * This class includes an implementation for unix Crypt an MD5 digest. 
35   * @see Password
36   * @author Greg Wilkins (gregw)
37   */
38  public abstract class Credential
39  {
40      /* ------------------------------------------------------------ */
41      /** Check a credential
42       * @param credentials The credential to check against. This may either be
43       * another Credential object, a Password object or a String which is
44       * interpreted by this credential. 
45       * @return True if the credentials indicated that the shared secret is
46       * known to both this Credential and the passed credential.
47       */
48      public abstract boolean check(Object credentials);
49  
50      /* ------------------------------------------------------------ */
51      /** Get a credential from a String.
52       * If the credential String starts with a known Credential type (eg
53       * "CRYPT:" or "MD5:" ) then a Credential of that type is returned. Else the
54       * credential is assumed to be a Password.
55       * @param credential String representation of the credential
56       * @return A Credential or Password instance.
57       */
58      public static Credential getCredential(String credential)
59      {
60          if (credential.startsWith(Crypt.__TYPE))
61              return new Crypt(credential);
62          if (credential.startsWith(MD5.__TYPE))
63              return new MD5(credential);
64          
65          return new Password(credential);
66      }
67  
68  
69      /* ------------------------------------------------------------ */
70      /** Unix Crypt Credentials
71       */
72      public static class Crypt extends Credential
73      {
74          public static final String __TYPE="CRYPT:";
75          
76          private String _cooked;
77          Crypt(String cooked)
78          {
79              _cooked=cooked.startsWith(Crypt.__TYPE)
80                  ?cooked.substring(__TYPE.length())
81                  :cooked;
82          }
83          
84          public boolean check(Object credentials)
85          {
86              if (!(credentials instanceof String) &&
87                  !(credentials instanceof Password))
88                  Log.warn("Can't check "+credentials.getClass()+" against CRYPT");
89              
90              String passwd = credentials.toString();
91              return _cooked.equals(UnixCrypt.crypt(passwd,_cooked));
92          }
93  
94          public static String crypt(String user,String pw)
95          {
96              return "CRYPT:"+UnixCrypt.crypt(pw,user);
97          }
98      }
99      
100     /* ------------------------------------------------------------ */
101     /** MD5 Credentials
102      */
103     public static class MD5 extends Credential
104     {
105         public static final String __TYPE="MD5:";
106         public static final Object __md5Lock = new Object();
107         private static MessageDigest __md;
108         
109         private byte[] _digest;
110         
111         /* ------------------------------------------------------------ */
112         MD5(String digest)
113         {
114             digest=digest.startsWith(__TYPE)
115                 ?digest.substring(__TYPE.length())
116                 :digest;
117             _digest=TypeUtil.parseBytes(digest,16);
118         }
119         
120         /* ------------------------------------------------------------ */
121         public byte[] getDigest()
122         {
123             return _digest;
124         }
125         
126         /* ------------------------------------------------------------ */
127         public boolean check(Object credentials)
128         {
129             try
130             {
131                 byte[] digest=null;
132                 
133                 if (credentials instanceof Password ||
134                     credentials instanceof String)
135                 {
136                     synchronized(__md5Lock)
137                     {
138                         if (__md==null)
139                             __md = MessageDigest.getInstance("MD5");
140                         __md.reset();
141                         __md.update(credentials.toString().getBytes(StringUtil.__ISO_8859_1));
142                         digest=__md.digest();
143                     }
144                     if (digest==null || digest.length!=_digest.length)
145                         return false;
146                     for (int i=0;i<digest.length;i++)
147                         if (digest[i]!=_digest[i])
148                             return false;
149                     return true;
150                 }
151                 else if (credentials instanceof MD5)
152                 {
153                     MD5 md5 = (MD5)credentials;
154                     if (_digest.length!=md5._digest.length)
155                         return false;
156                     for (int i=0;i<_digest.length;i++)
157                         if (_digest[i]!=md5._digest[i])
158                             return false;
159                     return true;
160                 }
161                 else if(credentials instanceof Credential)
162                 {
163                     // Allow credential to attempt check - i.e. this'll work
164                     // for DigestAuthenticator$Digest credentials
165                     return ((Credential)credentials).check(this);
166                 }
167                 else
168                 {
169                     Log.warn("Can't check "+credentials.getClass()+" against MD5");
170                     return false;
171                 }
172             }
173             catch (Exception e)
174             {
175                 Log.warn(e);
176                 return false;
177             }
178         }
179 
180         /* ------------------------------------------------------------ */
181         public static String digest(String password)
182         {
183             try
184             {
185                 byte[] digest;
186                 synchronized(__md5Lock)
187                 {
188                     if (__md==null)
189                     {
190                         try{__md = MessageDigest.getInstance("MD5");}
191                         catch (Exception e ) {Log.warn(e);return null;}
192                     }
193                     
194                     __md.reset();
195                     __md.update(password.getBytes(StringUtil.__ISO_8859_1));
196                     digest=__md.digest();
197                 }
198                 
199                 return __TYPE+TypeUtil.toString(digest,16);
200             }
201             catch (Exception e)
202             {
203                 Log.warn(e);
204                 return null;
205             }
206         }
207     }
208 }