1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/DigestScheme.java,v 1.4 2003/05/26 22:07:22 oglueck Exp $
3 * $Revision: 1.4 $
4 * $Date: 2003/05/26 22:07:22 $
5 *
6 * ====================================================================
7 *
8 * The Apache Software License, Version 1.1
9 *
10 * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
11 * reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 *
25 * 3. The end-user documentation included with the redistribution, if
26 * any, must include the following acknowlegement:
27 * "This product includes software developed by the
28 * Apache Software Foundation (http://www.apache.org/)."
29 * Alternately, this acknowlegement may appear in the software itself,
30 * if and wherever such third-party acknowlegements normally appear.
31 *
32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33 * Foundation" must not be used to endorse or promote products derived
34 * from this software without prior written permission. For written
35 * permission, please contact apache@apache.org.
36 *
37 * 5. Products derived from this software may not be called "Apache"
38 * nor may "Apache" appear in their names without prior written
39 * permission of the Apache Group.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 * ====================================================================
54 *
55 * This software consists of voluntary contributions made by many
56 * individuals on behalf of the Apache Software Foundation. For more
57 * information on the Apache Software Foundation, please see
58 * <http://www.apache.org/>.
59 *
60 * [Additional notices, if required by prior licensing conditions]
61 *
62 */
63
64 package org.apache.commons.httpclient.auth;
65
66 import java.util.Map;
67 import java.security.MessageDigest;
68
69 import org.apache.commons.httpclient.HttpConstants;
70 import org.apache.commons.httpclient.Credentials;
71 import org.apache.commons.httpclient.UsernamePasswordCredentials;
72 import org.apache.commons.logging.Log;
73 import org.apache.commons.logging.LogFactory;
74
75 /***
76 * <p>
77 * Digest authentication scheme as defined in RFC 2617.
78 * </p>
79 *
80 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
81 * @author Rodney Waldhoff
82 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
83 * @author Ortwin Gl�ck
84 * @author Sean C. Sullivan
85 * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
86 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
87 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
88 */
89
90 public class DigestScheme extends RFC2617Scheme {
91
92 /*** Log object for this class. */
93 private static final Log LOG = LogFactory.getLog(DigestScheme.class);
94
95 /***
96 * Hexa values used when creating 32 character long digest in HTTP DigestScheme
97 * in case of authentication.
98 *
99 * @see #encode(byte[])
100 */
101 private static final char[] HEXADECIMAL = {
102 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
103 'e', 'f'
104 };
105
106 /***
107 * Constructor for the digest authentication scheme.
108 *
109 * @param challenge The authentication challenge
110 *
111 * @throws MalformedChallengeException is thrown if the authentication challenge
112 * is malformed
113 */
114 public DigestScheme(final String challenge)
115 throws MalformedChallengeException {
116 super(challenge);
117 this.getParameters().put("nc", "00000001");
118 }
119
120
121 /***
122 * Returns textual designation of the digest authentication scheme.
123 *
124 * @return <code>digest</code>
125 */
126 public String getSchemeName() {
127 return "digest";
128 }
129
130 /***
131 * Produces a digest authorization string for the given set of
132 * {@link Credentials}, method name and URI.
133 *
134 * @param credentials A set of credentials to be used for athentication
135 * @param method the name of the method that requires authorization.
136 * @param uri The URI for which authorization is needed.
137 *
138 * @throws AuthenticationException if authorization string cannot
139 * be generated due to an authentication failure
140 *
141 * @return a digest authorization string
142 *
143 * @see org.apache.commons.httpclient.HttpMethod#getName()
144 * @see org.apache.commons.httpclient.HttpMethod#getPath()
145 */
146 public String authenticate(Credentials credentials, String method, String uri)
147 throws AuthenticationException {
148
149 LOG.trace("enter DigestScheme.authenticate(Credentials, String, String)");
150
151 UsernamePasswordCredentials usernamepassword = null;
152 try {
153 usernamepassword = (UsernamePasswordCredentials) credentials;
154 } catch (ClassCastException e) {
155 throw new AuthenticationException(
156 "Credentials cannot be used for basic authentication: "
157 + credentials.toString());
158 }
159 this.getParameters().put("cnonce", createCnonce());
160 this.getParameters().put("methodname", method);
161 this.getParameters().put("uri", uri);
162 return DigestScheme.authenticate(usernamepassword, getParameters());
163 }
164
165 /***
166 * Produces a digest authorization string for the given set of
167 * {@link UsernamePasswordCredentials} and authetication parameters.
168 *
169 * @param credentials Credentials to create the digest with
170 * @param params The authetication parameters. The following
171 * parameters are expected: <code>uri</code>, <code>realm</code>,
172 * <code>nonce</code>, <code>nc</code>, <code>cnonce</code>,
173 * <code>qop</code>, <code>methodname</code>.
174 *
175 * @return a digest authorization string
176 *
177 * @throws AuthenticationException if authorization string cannot
178 * be generated due to an authentication failure
179 */
180 public static String authenticate(UsernamePasswordCredentials credentials,
181 Map params) throws AuthenticationException {
182
183 LOG.trace("enter DigestScheme.authenticate(UsernamePasswordCredentials, Map)");
184
185 String digest = createDigest(credentials.getUserName(),
186 credentials.getPassword(), params);
187
188 return "Digest " + createDigestHeader(credentials.getUserName(),
189 params, digest);
190 }
191
192 /***
193 * Creates an MD5 response digest.
194 *
195 * @param uname Username
196 * @param pwd Password
197 * @param params The parameters necessary to construct the digest.
198 * The following parameters are expected: <code>uri</code>,
199 * <code>realm</code>, <code>nonce</code>, <code>nc</code>,
200 * <code>cnonce</code>, <code>qop</code>, <code>methodname</code>.
201 *
202 * @return The created digest as string. This will be the response tag's
203 * value in the Authentication HTTP header.
204 * @throws AuthenticationException when MD5 is an unsupported algorithm
205 */
206 public static String createDigest(String uname, String pwd,
207 Map params) throws AuthenticationException {
208
209 LOG.trace("enter DigestScheme.createDigest(String, String, Map)");
210
211 final String digAlg = "MD5";
212
213 // Collecting required tokens
214 String uri = (String) params.get("uri");
215 String realm = (String) params.get("realm");
216 String nonce = (String) params.get("nonce");
217 String nc = (String) params.get("nc");
218 String cnonce = (String) params.get("cnonce");
219 String qop = (String) params.get("qop");
220 String method = (String) params.get("methodname");
221
222 if (qop != null) {
223 qop = "auth";
224 }
225
226 MessageDigest md5Helper;
227
228 try {
229 md5Helper = MessageDigest.getInstance(digAlg);
230 } catch (Exception e) {
231 throw new AuthenticationException(
232 "Unsupported algorithm in HTTP Digest authentication: "
233 + digAlg);
234 }
235
236 // Calculating digest according to rfc 2617
237 String a2 = method + ":" + uri;
238 String md5a2 = encode(md5Helper.digest(HttpConstants.getBytes(a2)));
239 String digestValue = uname + ":" + realm + ":" + pwd;
240 String md5a1
241 = encode(md5Helper.digest(HttpConstants.getBytes(digestValue)));
242 String serverDigestValue;
243
244 if (qop == null) {
245 serverDigestValue = md5a1 + ":" + nonce + ":" + md5a2;
246 } else {
247 serverDigestValue = md5a1 + ":" + nonce + ":" + nc + ":" + cnonce
248 + ":" + qop + ":" + md5a2;
249 }
250
251 String serverDigest =
252 encode(md5Helper.digest(HttpConstants.getBytes(serverDigestValue)));
253
254 return serverDigest;
255 }
256
257 /***
258 * Creates digest-response header as defined in RFC2617.
259 *
260 * @param uname Username
261 * @param params The parameters necessary to construct the digest header.
262 * The following parameters are expected: <code>uri</code>,
263 * <code>realm</code>, <code>nonce</code>, <code>nc</code>,
264 * <code>cnonce</code>, <code>qop</code>, <code>methodname</code>.
265 * @param digest The response tag's value as String.
266 *
267 * @return The digest-response as String.
268 */
269 public static String createDigestHeader(String uname, Map params,
270 String digest) {
271
272 LOG.trace("enter DigestScheme.createDigestHeader(String, Map, "
273 + "String)");
274
275 StringBuffer sb = new StringBuffer();
276 String uri = (String) params.get("uri");
277 String realm = (String) params.get("realm");
278 String nonce = (String) params.get("nonce");
279 String nc = (String) params.get("nc");
280 String cnonce = (String) params.get("cnonce");
281 String opaque = (String) params.get("opaque");
282 String response = digest;
283 String qop = (String) params.get("qop");
284
285 if (qop != null) {
286 qop = "auth"; //we only support auth
287 }
288
289 String algorithm = "MD5"; //we only support MD5
290
291 sb.append("username=\"" + uname + "\"")
292 .append(", realm=\"" + realm + "\"")
293 .append(", nonce=\"" + nonce + "\"").append(", uri=\"" + uri + "\"")
294 .append(((qop == null) ? "" : ", qop=\"" + qop + "\""))
295 .append(", algorithm=\"" + algorithm + "\"")
296 .append(((qop == null) ? "" : ", nc=" + nc))
297 .append(((qop == null) ? "" : ", cnonce=\"" + cnonce + "\""))
298 .append(", response=\"" + response + "\"")
299 .append((opaque == null) ? "" : ", opaque=\"" + opaque + "\"");
300
301 return sb.toString();
302 }
303
304
305 /***
306 * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long
307 * <CODE>String</CODE> according to RFC 2617.
308 *
309 * @param binaryData array containing the digest
310 * @return encoded MD5, or <CODE>null</CODE> if encoding failed
311 */
312 private static String encode(byte[] binaryData) {
313 LOG.trace("enter DigestScheme.encode(byte[])");
314
315 if (binaryData.length != 16) {
316 return null;
317 }
318
319 char[] buffer = new char[32];
320 for (int i = 0; i < 16; i++) {
321 int low = (int) (binaryData[i] & 0x0f);
322 int high = (int) ((binaryData[i] & 0xf0) >> 4);
323 buffer[i * 2] = HEXADECIMAL[high];
324 buffer[(i * 2) + 1] = HEXADECIMAL[low];
325 }
326
327 return new String(buffer);
328 }
329
330
331 /***
332 * Creates a random cnonce value based on the current time.
333 *
334 * @return The cnonce value as String.
335 * @throws AuthenticationException if MD5 algorithm is not supported.
336 */
337 public static String createCnonce() throws AuthenticationException {
338 LOG.trace("enter DigestScheme.createCnonce()");
339
340 String cnonce;
341 final String digAlg = "MD5";
342 MessageDigest md5Helper;
343
344 try {
345 md5Helper = MessageDigest.getInstance(digAlg);
346 } catch (Exception e) {
347 throw new AuthenticationException(
348 "Unsupported algorithm in HTTP Digest authentication: "
349 + digAlg);
350 }
351
352 cnonce = Long.toString(System.currentTimeMillis());
353 cnonce = encode(md5Helper.digest(HttpConstants.getBytes(cnonce)));
354
355 return cnonce;
356 }
357 }
This page was automatically generated by Maven