1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.util.security; 20 21 import java.io.Serializable; 22 import java.security.MessageDigest; 23 24 import org.eclipse.jetty.util.StringUtil; 25 import org.eclipse.jetty.util.TypeUtil; 26 import org.eclipse.jetty.util.log.Log; 27 import org.eclipse.jetty.util.log.Logger; 28 29 /* ------------------------------------------------------------ */ 30 /** 31 * Credentials. The Credential class represents an abstract mechanism for 32 * checking authentication credentials. A credential instance either represents 33 * a secret, or some data that could only be derived from knowing the secret. 34 * <p> 35 * Often a Credential is related to a Password via a one way algorithm, so while 36 * a Password itself is a Credential, a UnixCrypt or MD5 digest of a a password 37 * is only a credential that can be checked against the password. 38 * <p> 39 * This class includes an implementation for unix Crypt an MD5 digest. 40 * 41 * @see Password 42 * 43 */ 44 public abstract class Credential implements Serializable 45 { 46 private static final Logger LOG = Log.getLogger(Credential.class); 47 48 private static final long serialVersionUID = -7760551052768181572L; 49 50 /* ------------------------------------------------------------ */ 51 /** 52 * Check a credential 53 * 54 * @param credentials The credential to check against. This may either be 55 * another Credential object, a Password object or a String 56 * which is interpreted by this credential. 57 * @return True if the credentials indicated that the shared secret is known 58 * to both this Credential and the passed credential. 59 */ check(Object credentials)60 public abstract boolean check(Object credentials); 61 62 /* ------------------------------------------------------------ */ 63 /** 64 * Get a credential from a String. If the credential String starts with a 65 * known Credential type (eg "CRYPT:" or "MD5:" ) then a Credential of that 66 * type is returned. Else the credential is assumed to be a Password. 67 * 68 * @param credential String representation of the credential 69 * @return A Credential or Password instance. 70 */ getCredential(String credential)71 public static Credential getCredential(String credential) 72 { 73 if (credential.startsWith(Crypt.__TYPE)) return new Crypt(credential); 74 if (credential.startsWith(MD5.__TYPE)) return new MD5(credential); 75 76 return new Password(credential); 77 } 78 79 /* ------------------------------------------------------------ */ 80 /** 81 * Unix Crypt Credentials 82 */ 83 public static class Crypt extends Credential 84 { 85 private static final long serialVersionUID = -2027792997664744210L; 86 87 public static final String __TYPE = "CRYPT:"; 88 89 private final String _cooked; 90 Crypt(String cooked)91 Crypt(String cooked) 92 { 93 _cooked = cooked.startsWith(Crypt.__TYPE) ? cooked.substring(__TYPE.length()) : cooked; 94 } 95 96 @Override check(Object credentials)97 public boolean check(Object credentials) 98 { 99 if (credentials instanceof char[]) 100 credentials=new String((char[])credentials); 101 if (!(credentials instanceof String) && !(credentials instanceof Password)) 102 LOG.warn("Can't check " + credentials.getClass() + " against CRYPT"); 103 104 String passwd = credentials.toString(); 105 return _cooked.equals(UnixCrypt.crypt(passwd, _cooked)); 106 } 107 crypt(String user, String pw)108 public static String crypt(String user, String pw) 109 { 110 return "CRYPT:" + UnixCrypt.crypt(pw, user); 111 } 112 } 113 114 /* ------------------------------------------------------------ */ 115 /** 116 * MD5 Credentials 117 */ 118 public static class MD5 extends Credential 119 { 120 private static final long serialVersionUID = 5533846540822684240L; 121 122 public static final String __TYPE = "MD5:"; 123 124 public static final Object __md5Lock = new Object(); 125 126 private static MessageDigest __md; 127 128 private final byte[] _digest; 129 130 /* ------------------------------------------------------------ */ MD5(String digest)131 MD5(String digest) 132 { 133 digest = digest.startsWith(__TYPE) ? digest.substring(__TYPE.length()) : digest; 134 _digest = TypeUtil.parseBytes(digest, 16); 135 } 136 137 /* ------------------------------------------------------------ */ getDigest()138 public byte[] getDigest() 139 { 140 return _digest; 141 } 142 143 /* ------------------------------------------------------------ */ 144 @Override check(Object credentials)145 public boolean check(Object credentials) 146 { 147 try 148 { 149 byte[] digest = null; 150 151 if (credentials instanceof char[]) 152 credentials=new String((char[])credentials); 153 if (credentials instanceof Password || credentials instanceof String) 154 { 155 synchronized (__md5Lock) 156 { 157 if (__md == null) __md = MessageDigest.getInstance("MD5"); 158 __md.reset(); 159 __md.update(credentials.toString().getBytes(StringUtil.__ISO_8859_1)); 160 digest = __md.digest(); 161 } 162 if (digest == null || digest.length != _digest.length) return false; 163 for (int i = 0; i < digest.length; i++) 164 if (digest[i] != _digest[i]) return false; 165 return true; 166 } 167 else if (credentials instanceof MD5) 168 { 169 MD5 md5 = (MD5) credentials; 170 if (_digest.length != md5._digest.length) return false; 171 for (int i = 0; i < _digest.length; i++) 172 if (_digest[i] != md5._digest[i]) return false; 173 return true; 174 } 175 else if (credentials instanceof Credential) 176 { 177 // Allow credential to attempt check - i.e. this'll work 178 // for DigestAuthModule$Digest credentials 179 return ((Credential) credentials).check(this); 180 } 181 else 182 { 183 LOG.warn("Can't check " + credentials.getClass() + " against MD5"); 184 return false; 185 } 186 } 187 catch (Exception e) 188 { 189 LOG.warn(e); 190 return false; 191 } 192 } 193 194 /* ------------------------------------------------------------ */ digest(String password)195 public static String digest(String password) 196 { 197 try 198 { 199 byte[] digest; 200 synchronized (__md5Lock) 201 { 202 if (__md == null) 203 { 204 try 205 { 206 __md = MessageDigest.getInstance("MD5"); 207 } 208 catch (Exception e) 209 { 210 LOG.warn(e); 211 return null; 212 } 213 } 214 215 __md.reset(); 216 __md.update(password.getBytes(StringUtil.__ISO_8859_1)); 217 digest = __md.digest(); 218 } 219 220 return __TYPE + TypeUtil.toString(digest, 16); 221 } 222 catch (Exception e) 223 { 224 LOG.warn(e); 225 return null; 226 } 227 } 228 } 229 } 230