001: // ========================================================================
002: // Copyright 1998-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.security;
016:
017: import java.security.MessageDigest;
018:
019: import org.mortbay.log.Log;
020: import org.mortbay.util.StringUtil;
021: import org.mortbay.util.TypeUtil;
022:
023: /* ------------------------------------------------------------ */
024: /** Credentials.
025: * The Credential class represents an abstract mechanism for checking
026: * authentication credentials. A credential instance either represents a
027: * secret, or some data that could only be derived from knowing the secret.
028: * <p>
029: * Often a Credential is related to a Password via a one way algorithm, so
030: * while a Password itself is a Credential, a UnixCrypt or MD5 digest of a
031: * a password is only a credential that can be checked against the password.
032: * <p>
033: * This class includes an implementation for unix Crypt an MD5 digest.
034: * @see Password
035: * @author Greg Wilkins (gregw)
036: */
037: public abstract class Credential {
038: /* ------------------------------------------------------------ */
039: /** Check a credential
040: * @param credentials The credential to check against. This may either be
041: * another Credential object, a Password object or a String which is
042: * interpreted by this credential.
043: * @return True if the credentials indicated that the shared secret is
044: * known to both this Credential and the passed credential.
045: */
046: public abstract boolean check(Object credentials);
047:
048: /* ------------------------------------------------------------ */
049: /** Get a credential from a String.
050: * If the credential String starts with a known Credential type (eg
051: * "CRYPT:" or "MD5:" ) then a Credential of that type is returned. Else the
052: * credential is assumed to be a Password.
053: * @param credential String representation of the credential
054: * @return A Credential or Password instance.
055: */
056: public static Credential getCredential(String credential) {
057: if (credential.startsWith(Crypt.__TYPE))
058: return new Crypt(credential);
059: if (credential.startsWith(MD5.__TYPE))
060: return new MD5(credential);
061:
062: return new Password(credential);
063: }
064:
065: /* ------------------------------------------------------------ */
066: /** Unix Crypt Credentials
067: */
068: public static class Crypt extends Credential {
069: public static final String __TYPE = "CRYPT:";
070:
071: private String _cooked;
072:
073: Crypt(String cooked) {
074: _cooked = cooked.startsWith(Crypt.__TYPE) ? cooked
075: .substring(__TYPE.length()) : cooked;
076: }
077:
078: public boolean check(Object credentials) {
079: if (!(credentials instanceof String)
080: && !(credentials instanceof Password))
081: Log.warn("Can't check " + credentials.getClass()
082: + " against CRYPT");
083:
084: String passwd = credentials.toString();
085: return _cooked.equals(UnixCrypt.crypt(passwd, _cooked));
086: }
087:
088: public static String crypt(String user, String pw) {
089: return "CRYPT:" + UnixCrypt.crypt(pw, user);
090: }
091: }
092:
093: /* ------------------------------------------------------------ */
094: /** MD5 Credentials
095: */
096: public static class MD5 extends Credential {
097: public static final String __TYPE = "MD5:";
098: private static MessageDigest __md;
099:
100: private byte[] _digest;
101:
102: /* ------------------------------------------------------------ */
103: MD5(String digest) {
104: digest = digest.startsWith(__TYPE) ? digest
105: .substring(__TYPE.length()) : digest;
106: _digest = TypeUtil.parseBytes(digest, 16);
107: }
108:
109: /* ------------------------------------------------------------ */
110: public byte[] getDigest() {
111: return _digest;
112: }
113:
114: /* ------------------------------------------------------------ */
115: public boolean check(Object credentials) {
116: try {
117: byte[] digest = null;
118:
119: if (credentials instanceof Password
120: || credentials instanceof String) {
121: synchronized (__TYPE) {
122: if (__md == null)
123: __md = MessageDigest.getInstance("MD5");
124: __md.reset();
125: __md.update(credentials.toString().getBytes(
126: StringUtil.__ISO_8859_1));
127: digest = __md.digest();
128: }
129: if (digest == null
130: || digest.length != _digest.length)
131: return false;
132: for (int i = 0; i < digest.length; i++)
133: if (digest[i] != _digest[i])
134: return false;
135: return true;
136: } else if (credentials instanceof MD5) {
137: MD5 md5 = (MD5) credentials;
138: if (_digest.length != md5._digest.length)
139: return false;
140: for (int i = 0; i < _digest.length; i++)
141: if (_digest[i] != md5._digest[i])
142: return false;
143: return true;
144: } else if (credentials instanceof Credential) {
145: // Allow credential to attempt check - i.e. this'll work
146: // for DigestAuthenticator$Digest credentials
147: return ((Credential) credentials).check(this );
148: } else {
149: Log.warn("Can't check " + credentials.getClass()
150: + " against MD5");
151: return false;
152: }
153: } catch (Exception e) {
154: Log.warn(e);
155: return false;
156: }
157: }
158:
159: /* ------------------------------------------------------------ */
160: public static String digest(String password) {
161: try {
162: byte[] digest;
163: synchronized (__TYPE) {
164: if (__md == null) {
165: try {
166: __md = MessageDigest.getInstance("MD5");
167: } catch (Exception e) {
168: Log.warn(e);
169: return null;
170: }
171: }
172:
173: __md.reset();
174: __md.update(password
175: .getBytes(StringUtil.__ISO_8859_1));
176: digest = __md.digest();
177: }
178:
179: return __TYPE + TypeUtil.toString(digest, 16);
180: } catch (Exception e) {
181: Log.warn(e);
182: return null;
183: }
184: }
185: }
186: }
|