View Javadoc
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