blob: de4af1fe09213687f5af496fc04fdab2a1176fe5 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 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.SaslException;
29import javax.security.sasl.Sasl;
30
31// For HMAC_MD5
32import java.security.NoSuchAlgorithmException;
33import java.security.MessageDigest;
34
35import java.util.logging.Logger;
36
37/**
38 * Base class for implementing CRAM-MD5 client and server mechanisms.
39 *
40 * @author Vincent Ryan
41 * @author Rosanna Lee
42 */
43abstract class CramMD5Base {
44 protected boolean completed = false;
45 protected boolean aborted = false;
46 protected byte[] pw;
47
48 protected CramMD5Base() {
49 initLogger();
50 }
51
52 /**
53 * Retrieves this mechanism's name.
54 *
55 * @return The string "CRAM-MD5".
56 */
57 public String getMechanismName() {
58 return "CRAM-MD5";
59 }
60
61 /**
62 * Determines whether this mechanism has completed.
63 * CRAM-MD5 completes after processing one challenge from the server.
64 *
65 * @return true if has completed; false otherwise;
66 */
67 public boolean isComplete() {
68 return completed;
69 }
70
71 /**
72 * Unwraps the incoming buffer. CRAM-MD5 supports no security layer.
73 *
74 * @throws SaslException If attempt to use this method.
75 */
76 public byte[] unwrap(byte[] incoming, int offset, int len)
77 throws SaslException {
78 if (completed) {
79 throw new IllegalStateException(
80 "CRAM-MD5 supports neither integrity nor privacy");
81 } else {
82 throw new IllegalStateException(
83 "CRAM-MD5 authentication not completed");
84 }
85 }
86
87 /**
88 * Wraps the outgoing buffer. CRAM-MD5 supports no security layer.
89 *
90 * @throws SaslException If attempt to use this method.
91 */
92 public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {
93 if (completed) {
94 throw new IllegalStateException(
95 "CRAM-MD5 supports neither integrity nor privacy");
96 } else {
97 throw new IllegalStateException(
98 "CRAM-MD5 authentication not completed");
99 }
100 }
101
102 /**
103 * Retrieves the negotiated property.
104 * This method can be called only after the authentication exchange has
105 * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, a
106 * <tt>SaslException</tt> is thrown.
107 *
108 * @return value of property; only QOP is applicable to CRAM-MD5.
109 * @exception IllegalStateException if this authentication exchange has not completed
110 */
111 public Object getNegotiatedProperty(String propName) {
112 if (completed) {
113 if (propName.equals(Sasl.QOP)) {
114 return "auth";
115 } else {
116 return null;
117 }
118 } else {
119 throw new IllegalStateException(
120 "CRAM-MD5 authentication not completed");
121 }
122 }
123
124 public void dispose() throws SaslException {
125 clearPassword();
126 }
127
128 protected void clearPassword() {
129 if (pw != null) {
130 // zero out password
131 for (int i = 0; i < pw.length; i++) {
132 pw[i] = (byte)0;
133 }
134 pw = null;
135 }
136 }
137
138 protected void finalize() {
139 clearPassword();
140 }
141
142 static private final int MD5_BLOCKSIZE = 64;
143 /**
144 * Hashes its input arguments according to HMAC-MD5 (RFC 2104)
145 * and returns the resulting digest in its ASCII representation.
146 *
147 * HMAC-MD5 function is described as follows:
148 *
149 * MD5(key XOR opad, MD5(key XOR ipad, text))
150 *
151 * where key is an n byte key
152 * ipad is the byte 0x36 repeated 64 times
153 * opad is the byte 0x5c repeated 64 times
154 * text is the data to be protected
155 */
156 final static String HMAC_MD5(byte[] key, byte[] text)
157 throws NoSuchAlgorithmException {
158
159 MessageDigest md5 = MessageDigest.getInstance("MD5");
160
161 /* digest the key if longer than 64 bytes */
162 if (key.length > 64) {
163 key = md5.digest(key);
164 }
165
166 byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */
167 byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */
168 byte[] digest;
169 int i;
170
171 /* store key in pads */
172 for (i = 0; i < MD5_BLOCKSIZE; i++) {
173 for ( ; i < key.length; i++) {
174 ipad[i] = key[i];
175 opad[i] = key[i];
176 }
177 ipad[i] = 0x00;
178 opad[i] = 0x00;
179 }
180
181 /* XOR key with pads */
182 for (i = 0; i < MD5_BLOCKSIZE; i++) {
183 ipad[i] ^= 0x36;
184 opad[i] ^= 0x5c;
185 }
186
187 /* inner MD5 */
188 md5.update(ipad);
189 md5.update(text);
190 digest = md5.digest();
191
192 /* outer MD5 */
193 md5.update(opad);
194 md5.update(digest);
195 digest = md5.digest();
196
197 // Get character representation of digest
198 StringBuffer digestString = new StringBuffer();
199
200 for (i = 0; i < digest.length; i++) {
201 if ((digest[i] & 0x000000ff) < 0x10) {
202 digestString.append("0" +
203 Integer.toHexString(digest[i] & 0x000000ff));
204 } else {
205 digestString.append(
206 Integer.toHexString(digest[i] & 0x000000ff));
207 }
208 }
209
210 return (digestString.toString());
211 }
212
213 /**
214 * Sets logger field.
215 */
216 private static synchronized void initLogger() {
217 if (logger == null) {
218 logger = Logger.getLogger(SASL_LOGGER_NAME);
219 }
220 }
221 /**
222 * Logger for debug messages
223 */
224 private static final String SASL_LOGGER_NAME = "javax.security.sasl";
225 protected static Logger logger; // set in initLogger(); lazily loads logger
226}