130 lines · 4.7 KB · Java
RawDownload
/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.security.sasl; import javax.security.sasl.*;import java.security.NoSuchAlgorithmException; import java.util.logging.Logger;import java.util.logging.Level; /**  * Implements the CRAM-MD5 SASL client-side mechanism.  * (<A HREF="http://www.ietf.org/rfc/rfc2195.txt">RFC 2195</A>).  * CRAM-MD5 has no initial response. It receives bytes from  * the server as a challenge, which it hashes by using MD5 and the password.  * It concatenates the authentication ID with this result and returns it  * as the response to the challenge. At that point, the exchange is complete.  *  * @author Vincent Ryan  * @author Rosanna Lee  */final class CramMD5Client extends CramMD5Base implements SaslClient {    private String username;     /**     * Creates a SASL mechanism with client credentials that it needs     * to participate in CRAM-MD5 authentication exchange with the server.     *     * @param authID A  non-null string representing the principal     * being authenticated.     *     * @param pw A non-null String or byte[]     * containing the password. If it is an array, it is first cloned.     */    CramMD5Client(String authID, byte[] pw) throws SaslException {        if (authID == null || pw == null) {            throw new SaslException(                "CRAM-MD5: authentication ID and password must be specified");        }         username = authID;        this.pw = pw;  // caller should have already cloned    }     /**     * CRAM-MD5 has no initial response.     */    public boolean hasInitialResponse() {        return false;    }     /**     * Processes the challenge data.     *     * The server sends a challenge data using which the client must     * compute an MD5-digest with its password as the key.     *     * @param challengeData A non-null byte array containing the challenge     *        data from the server.     * @return A non-null byte array containing the response to be sent to     *        the server.     * @throws SaslException If platform does not have MD5 support     * @throw IllegalStateException if this method is invoked more than once.     */    public byte[] evaluateChallenge(byte[] challengeData)        throws SaslException {         // See if we've been here before        if (completed) {            throw new IllegalStateException(                "CRAM-MD5 authentication already completed");        }         if (aborted) {            throw new IllegalStateException(                "CRAM-MD5 authentication previously aborted due to error");        }         // generate a keyed-MD5 digest from the user's password and challenge.        try {            if (logger.isLoggable(Level.FINE)) {                logger.log(Level.FINE, "CRAMCLNT01:Received challenge: {0}",                    new String(challengeData, "UTF8"));            }             String digest = HMAC_MD5(pw, challengeData);             // clear it when we no longer need it            clearPassword();             // response is username + " " + digest            String resp = username + " " + digest;             logger.log(Level.FINE, "CRAMCLNT02:Sending response: {0}", resp);             completed = true;             return resp.getBytes("UTF8");        } catch (java.security.NoSuchAlgorithmException e) {            aborted = true;            throw new SaslException("MD5 algorithm not available on platform", e);        } catch (java.io.UnsupportedEncodingException e) {            aborted = true;            throw new SaslException("UTF8 not available on platform", e);        }    }}