001: /*
002: * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.security.sasl;
027:
028: import javax.security.sasl.*;
029: import java.security.NoSuchAlgorithmException;
030:
031: import java.util.logging.Logger;
032: import java.util.logging.Level;
033:
034: /**
035: * Implements the CRAM-MD5 SASL client-side mechanism.
036: * (<A HREF="ftp://ftp.isi.edu/in-notes/rfc2195.txt">RFC 2195</A>).
037: * CRAM-MD5 has no initial response. It receives bytes from
038: * the server as a challenge, which it hashes by using MD5 and the password.
039: * It concatenates the authentication ID with this result and returns it
040: * as the response to the challenge. At that point, the exchange is complete.
041: *
042: * @author Vincent Ryan
043: * @author Rosanna Lee
044: */
045: final class CramMD5Client extends CramMD5Base implements SaslClient {
046: private String username;
047:
048: /**
049: * Creates a SASL mechanism with client credentials that it needs
050: * to participate in CRAM-MD5 authentication exchange with the server.
051: *
052: * @param authID A non-null string representing the principal
053: * being authenticated.
054: *
055: * @param pw A non-null String or byte[]
056: * containing the password. If it is an array, it is first cloned.
057: */
058: CramMD5Client(String authID, byte[] pw) throws SaslException {
059: if (authID == null || pw == null) {
060: throw new SaslException(
061: "CRAM-MD5: authentication ID and password must be specified");
062: }
063:
064: username = authID;
065: this .pw = pw; // caller should have already cloned
066: }
067:
068: /**
069: * CRAM-MD5 has no initial response.
070: */
071: public boolean hasInitialResponse() {
072: return false;
073: }
074:
075: /**
076: * Processes the challenge data.
077: *
078: * The server sends a challenge data using which the client must
079: * compute an MD5-digest with its password as the key.
080: *
081: * @param challengeData A non-null byte array containing the challenge
082: * data from the server.
083: * @return A non-null byte array containing the response to be sent to
084: * the server.
085: * @throws SaslException If platform does not have MD5 support
086: * @throw IllegalStateException if this method is invoked more than once.
087: */
088: public byte[] evaluateChallenge(byte[] challengeData)
089: throws SaslException {
090:
091: // See if we've been here before
092: if (completed) {
093: throw new IllegalStateException(
094: "CRAM-MD5 authentication already completed");
095: }
096:
097: if (aborted) {
098: throw new IllegalStateException(
099: "CRAM-MD5 authentication previously aborted due to error");
100: }
101:
102: // generate a keyed-MD5 digest from the user's password and challenge.
103: try {
104: if (logger.isLoggable(Level.FINE)) {
105: logger.log(Level.FINE,
106: "CRAMCLNT01:Received challenge: {0}",
107: new String(challengeData, "UTF8"));
108: }
109:
110: String digest = HMAC_MD5(pw, challengeData);
111:
112: // clear it when we no longer need it
113: clearPassword();
114:
115: // response is username + " " + digest
116: String resp = username + " " + digest;
117:
118: logger.log(Level.FINE, "CRAMCLNT02:Sending response: {0}",
119: resp);
120:
121: completed = true;
122:
123: return resp.getBytes("UTF8");
124: } catch (java.security.NoSuchAlgorithmException e) {
125: aborted = true;
126: throw new SaslException(
127: "MD5 algorithm not available on platform", e);
128: } catch (java.io.UnsupportedEncodingException e) {
129: aborted = true;
130: throw new SaslException("UTF8 not available on platform", e);
131: }
132: }
133: }
|