1 /* 2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.security.sasl; 27 28 import javax.security.sasl.*; 29 import java.security.NoSuchAlgorithmException; 30 31 import java.util.logging.Logger; 32 import java.util.logging.Level; 33 34 /** 35 * Implements the CRAM-MD5 SASL client-side mechanism. 36 * (<A HREF="http://www.ietf.org/rfc/rfc2195.txt">RFC 2195</A>). 37 * CRAM-MD5 has no initial response. It receives bytes from 38 * the server as a challenge, which it hashes by using MD5 and the password. 39 * It concatenates the authentication ID with this result and returns it 40 * as the response to the challenge. At that point, the exchange is complete. 41 * 42 * @author Vincent Ryan 43 * @author Rosanna Lee 44 */ 45 final class CramMD5Client extends CramMD5Base implements SaslClient { 46 private String username; 47 48 /** 49 * Creates a SASL mechanism with client credentials that it needs 50 * to participate in CRAM-MD5 authentication exchange with the server. 51 * 52 * @param authID A non-null string representing the principal 53 * being authenticated. 54 * 55 * @param pw A non-null String or byte[] 56 * containing the password. If it is an array, it is first cloned. 57 */ 58 CramMD5Client(String authID, byte[] pw) throws SaslException { 59 if (authID == null || pw == null) { 60 throw new SaslException( 61 "CRAM-MD5: authentication ID and password must be specified"); 62 } 63 64 username = authID; 65 this.pw = pw; // caller should have already cloned 66 } 67 68 /** 69 * CRAM-MD5 has no initial response. 70 */ 71 public boolean hasInitialResponse() { 72 return false; 73 } 74 75 /** 76 * Processes the challenge data. 77 * 78 * The server sends a challenge data using which the client must 79 * compute an MD5-digest with its password as the key. 80 * 81 * @param challengeData A non-null byte array containing the challenge 82 * data from the server. 83 * @return A non-null byte array containing the response to be sent to 84 * the server. 85 * @throws SaslException If platform does not have MD5 support 86 * @throw IllegalStateException if this method is invoked more than once. 87 */ 88 public byte[] evaluateChallenge(byte[] challengeData) 89 throws SaslException { 90 91 // See if we've been here before 92 if (completed) { 93 throw new IllegalStateException( 94 "CRAM-MD5 authentication already completed"); 95 } 96 97 if (aborted) { 98 throw new IllegalStateException( 99 "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, "CRAMCLNT01:Received challenge: {0}", 106 new String(challengeData, "UTF8")); 107 } 108 109 String digest = HMAC_MD5(pw, challengeData); 110 111 // clear it when we no longer need it 112 clearPassword(); 113 114 // response is username + " " + digest 115 String resp = username + " " + digest; 116 117 logger.log(Level.FINE, "CRAMCLNT02:Sending response: {0}", resp); 118 119 completed = true; 120 121 return resp.getBytes("UTF8"); 122 } catch (java.security.NoSuchAlgorithmException e) { 123 aborted = true; 124 throw new SaslException("MD5 algorithm not available on platform", e); 125 } catch (java.io.UnsupportedEncodingException e) { 126 aborted = true; 127 throw new SaslException("UTF8 not available on platform", e); 128 } 129 } 130 }