1 19 20 package com.maverick.http; 21 22 import java.io.IOException ; 23 import java.util.Hashtable ; 24 import java.util.StringTokenizer ; 25 26 import com.maverick.crypto.digests.Hash; 27 import com.maverick.crypto.digests.MD5Digest; 28 29 33 public class DigestAuthentication extends HttpAuthenticator { 34 35 Hashtable params; 36 37 43 private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 44 45 46 private boolean complete; 47 48 private static final String NC = "00000001"; private static final int QOP_MISSING = 0; 53 private static final int QOP_AUTH_INT = 1; 54 private static final int QOP_AUTH = 2; 55 56 private int qopVariant = QOP_MISSING; 57 private String cnonce; 58 boolean isAuthenticated = false; 59 60 public DigestAuthentication(String uri, String host, int port, boolean secure) { 61 super("Digest", uri, host, port, secure); } 63 64 public void setChallenge(String challenge) { 65 try { 66 params = ParameterParser.extractParams(challenge); 67 68 boolean unsupportedQop = false; 69 String qop = (String ) params.get("qop"); if (qop != null) { 72 StringTokenizer tok = new StringTokenizer (qop, ","); while (tok.hasMoreTokens()) { 74 String variant = tok.nextToken().trim(); 75 if (variant.equals("auth")) { qopVariant = QOP_AUTH; 77 break; } else if (variant.equals("auth-int")) { qopVariant = QOP_AUTH_INT; 81 } else { 82 unsupportedQop = true; 83 } 84 } 85 } 86 87 this.cnonce = createCnonce(); 88 } catch (IOException ex) { 89 } 90 } 91 92 public boolean isStateless() { 93 return false; 94 } 95 96 103 public void authenticate(HttpRequest request, HttpMethod method) throws IOException { 104 params.put("methodname", method.getName()); params.put("uri", method.getURI()); 107 String serverDigest = createDigest(credentials.getUsername(), credentials.getPassword(), "US-ASCII"); 109 request.setHeaderField(authorizationHeader, "Digest " + createDigestHeader(credentials.getUsername(), serverDigest)); 111 } 112 113 119 public int processResponse(HttpResponse response) { 120 return (hasCompleted = response.getStatus() >= 200 && response.getStatus() < 400) ? AUTHENTICATION_COMPLETED 121 : AUTHENTICATION_FAILED; 122 } 123 124 private String createDigest(String uname, String pwd, String charset) throws IOException { 125 126 final String digAlg = "MD5"; 128 String uri = (String ) params.get("uri"); String realm = (String ) params.get("realm"); String nonce = (String ) params.get("nonce"); String qop = (String ) params.get("qop"); String method = (String ) params.get("methodname"); String algorithm = (String ) params.get("algorithm"); 136 if (algorithm == null) { 138 algorithm = "MD5"; } 140 141 if (qopVariant == QOP_AUTH_INT) { 142 throw new IOException (Messages.getString("DigestAuthentication.unsupportedQop")); } 144 145 Hash hash = new Hash(new MD5Digest()); 146 147 StringBuffer tmp = new StringBuffer (uname.length() + realm.length() + pwd.length() + 2); 149 tmp.append(uname); 150 tmp.append(':'); 151 tmp.append(realm); 152 tmp.append(':'); 153 tmp.append(pwd); 154 String a1 = tmp.toString(); 156 if (algorithm.equals("MD5-sess")) { 162 hash.putBytes(a1.getBytes("US-ASCII")); String tmp2 = encode(hash.doFinal()); 164 StringBuffer tmp3 = new StringBuffer (tmp2.length() + nonce.length() + cnonce.length() + 2); 165 tmp3.append(tmp2); 166 tmp3.append(':'); 167 tmp3.append(nonce); 168 tmp3.append(':'); 169 tmp3.append(cnonce); 170 a1 = tmp3.toString(); 171 } else if (!algorithm.equals("MD5")) { 173 } 174 175 hash.reset(); 176 hash.putBytes(a1.getBytes("US-ASCII")); String md5a1 = encode(hash.doFinal()); 178 179 String a2 = null; 180 if (qopVariant == QOP_AUTH_INT) { 181 } else { 184 a2 = method + ":" + uri; } 186 187 hash.reset(); 188 hash.putBytes(a2.getBytes("US-ASCII")); String md5a2 = encode(hash.doFinal()); 190 191 String serverDigestValue; 193 if (qopVariant == QOP_MISSING) { 194 195 StringBuffer tmp2 = new StringBuffer (md5a1.length() + nonce.length() + md5a2.length()); 196 tmp2.append(md5a1); 197 tmp2.append(':'); 198 tmp2.append(nonce); 199 tmp2.append(':'); 200 tmp2.append(md5a2); 201 serverDigestValue = tmp2.toString(); 202 } else { 203 204 String qopOption = getQopVariantString(); 205 StringBuffer tmp2 = new StringBuffer (md5a1.length() + nonce.length() + NC.length() + cnonce.length() 206 + qopOption.length() + md5a2.length() + 5); 207 tmp2.append(md5a1); 208 tmp2.append(':'); 209 tmp2.append(nonce); 210 tmp2.append(':'); 211 tmp2.append(NC); 212 tmp2.append(':'); 213 tmp2.append(cnonce); 214 tmp2.append(':'); 215 tmp2.append(qopOption); 216 tmp2.append(':'); 217 tmp2.append(md5a2); 218 serverDigestValue = tmp2.toString(); 219 } 220 221 hash.reset(); 222 hash.putBytes(serverDigestValue.getBytes("US-ASCII")); String serverDigest = encode(hash.doFinal()); 224 225 return serverDigest; 226 } 227 228 public static String createCnonce() { 229 230 String cnonce; 231 Hash hash = new Hash(new MD5Digest()); 232 233 cnonce = Long.toString(System.currentTimeMillis()); 234 235 hash.putBytes(cnonce.getBytes()); 236 cnonce = encode(hash.doFinal()); 237 238 return cnonce; 239 } 240 241 private static String encode(byte[] binaryData) { 242 243 if (binaryData.length != 16) { 244 return null; 245 } 246 247 char[] buffer = new char[32]; 248 for (int i = 0; i < 16; i++) { 249 int low = (int) (binaryData[i] & 0x0f); 250 int high = (int) ((binaryData[i] & 0xf0) >> 4); 251 buffer[i * 2] = HEXADECIMAL[high]; 252 buffer[(i * 2) + 1] = HEXADECIMAL[low]; 253 } 254 255 return new String (buffer); 256 } 257 258 private String createDigestHeader(String uname, String digest) throws IOException { 259 260 StringBuffer sb = new StringBuffer (); 261 String uri = (String ) params.get("uri"); String realm = (String ) params.get("realm"); String nonce = (String ) params.get("nonce"); String nc = (String ) params.get("nc"); String opaque = (String ) params.get("opaque"); String response = digest; 267 String qop = (String ) params.get("qop"); String algorithm = (String ) params.get("algorithm"); 270 sb.append("username=\"" + uname + "\"") .append(", realm=\"" + realm + "\"") .append(", nonce=\"" + nonce + "\"").append(", uri=\"" + uri + "\"") .append(", response=\"" + response + "\""); if (qopVariant != QOP_MISSING) { 275 sb.append(", qop=\"" + getQopVariantString() + "\"") .append(", nc=" + NC) .append(", cnonce=\"" + cnonce + "\""); } 279 if (algorithm != null) { 280 sb.append(", algorithm=\"" + algorithm + "\""); } 282 if (opaque != null) { 283 sb.append(", opaque=\"" + opaque + "\""); } 285 return sb.toString(); 286 } 287 288 private String getQopVariantString() { 289 String qopOption; 290 if (qopVariant == QOP_AUTH_INT) { 291 qopOption = "auth-int"; } else { 293 qopOption = "auth"; } 295 return qopOption; 296 } 297 298 public String getInformation() { 299 return (String )params.get("realm"); 300 } 301 302 } 303 | Popular Tags |