blob: d3126fc9a70d1045ff90a8b9f1fb6d225f637328 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.security.sasl;
27
28import javax.security.sasl.*;
29import java.security.NoSuchAlgorithmException;
30
31import java.util.logging.Logger;
32import java.util.logging.Level;
33
34/**
35 * Implements the CRAM-MD5 SASL client-side mechanism.
36 * (<A HREF="ftp://ftp.isi.edu/in-notes/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 */
45final 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}