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 }