History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/www/protocol/http/DigestAuthentication.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/www/protocol/http/DigestAuthentication.java+513−0
@@ -0,0 +1,513 @@
1/*
2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.net.www.protocol.http;
27
28import java.io.*;
29import java.net.URL;
30import java.net.ProtocolException;
31import java.net.PasswordAuthentication;
32import java.util.Arrays;
33import java.util.StringTokenizer;
34import java.util.Random;
35
36import sun.net.www.HeaderParser;
37import java.security.MessageDigest;
38import java.security.NoSuchAlgorithmException;
39import static sun.net.www.protocol.http.HttpURLConnection.HTTP_CONNECT;
40
41/**
42 * DigestAuthentication: Encapsulate an http server authentication using
43 * the "Digest" scheme, as described in RFC2069 and updated in RFC2617
44 *
45 * @author Bill Foote
46 */
47
48class DigestAuthentication extends AuthenticationInfo {
49
50 private static final long serialVersionUID = 100L;
51
52 private String authMethod;
53
54 // Authentication parameters defined in RFC2617.
55 // One instance of these may be shared among several DigestAuthentication
56 // instances as a result of a single authorization (for multiple domains)
57
58 static class Parameters implements java.io.Serializable {
59 private static final long serialVersionUID = -3584543755194526252L;
60
61 private boolean serverQop; // server proposed qop=auth
62 private String opaque;
63 private String cnonce;
64 private String nonce;
65 private String algorithm;
66 private int NCcount=0;
67
68 // The H(A1) string used for MD5-sess
69 private String cachedHA1;
70
71 // Force the HA1 value to be recalculated because the nonce has changed
72 private boolean redoCachedHA1 = true;
73
74 private static final int cnonceRepeat = 5;
75
76 private static final int cnoncelen = 40; /* number of characters in cnonce */
77
78 private static Random random;
79
80 static {
81 random = new Random();
82 }
83
84 Parameters () {
85 serverQop = false;
86 opaque = null;
87 algorithm = null;
88 cachedHA1 = null;
89 nonce = null;
90 setNewCnonce();
91 }
92
93 boolean authQop () {
94 return serverQop;
95 }
96 synchronized void incrementNC() {
97 NCcount ++;
98 }
99 synchronized int getNCCount () {
100 return NCcount;
101 }
102
103 int cnonce_count = 0;
104
105 /* each call increments the counter */
106 synchronized String getCnonce () {
107 if (cnonce_count >= cnonceRepeat) {
108 setNewCnonce();
109 }
110 cnonce_count++;
111 return cnonce;
112 }
113 synchronized void setNewCnonce () {
114 byte bb[] = new byte [cnoncelen/2];
115 char cc[] = new char [cnoncelen];
116 random.nextBytes (bb);
117 for (int i=0; i<(cnoncelen/2); i++) {
118 int x = bb[i] + 128;
119 cc[i*2]= (char) ('A'+ x/16);
120 cc[i*2+1]= (char) ('A'+ x%16);
121 }
122 cnonce = new String (cc, 0, cnoncelen);
123 cnonce_count = 0;
124 redoCachedHA1 = true;
125 }
126
127 synchronized void setQop (String qop) {
128 if (qop != null) {
129 StringTokenizer st = new StringTokenizer (qop, " ");
130 while (st.hasMoreTokens()) {
131 if (st.nextToken().equalsIgnoreCase ("auth")) {
132 serverQop = true;
133 return;
134 }
135 }
136 }
137 serverQop = false;
138 }
139
140 synchronized String getOpaque () { return opaque;}
141 synchronized void setOpaque (String s) { opaque=s;}
142
143 synchronized String getNonce () { return nonce;}
144
145 synchronized void setNonce (String s) {
146 if (!s.equals(nonce)) {
147 nonce=s;
148 NCcount = 0;
149 redoCachedHA1 = true;
150 }
151 }
152
153 synchronized String getCachedHA1 () {
154 if (redoCachedHA1) {
155 return null;
156 } else {
157 return cachedHA1;
158 }
159 }
160
161 synchronized void setCachedHA1 (String s) {
162 cachedHA1=s;
163 redoCachedHA1=false;
164 }
165
166 synchronized String getAlgorithm () { return algorithm;}
167 synchronized void setAlgorithm (String s) { algorithm=s;}
168 }
169
170 Parameters params;
171
172 /**
173 * Create a DigestAuthentication
174 */
175 public DigestAuthentication(boolean isProxy, URL url, String realm,
176 String authMethod, PasswordAuthentication pw,
177 Parameters params) {
178 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
179 AuthScheme.DIGEST,
180 url,
181 realm);
182 this.authMethod = authMethod;
183 this.pw = pw;
184 this.params = params;
185 }
186
187 public DigestAuthentication(boolean isProxy, String host, int port, String realm,
188 String authMethod, PasswordAuthentication pw,
189 Parameters params) {
190 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
191 AuthScheme.DIGEST,
192 host,
193 port,
194 realm);
195 this.authMethod = authMethod;
196 this.pw = pw;
197 this.params = params;
198 }
199
200 /**
201 * @return true if this authentication supports preemptive authorization
202 */
203 @Override
204 public boolean supportsPreemptiveAuthorization() {
205 return true;
206 }
207
208 /**
209 * Reclaculates the request-digest and returns it.
210 *
211 * <P> Used in the common case where the requestURI is simply the
212 * abs_path.
213 *
214 * @param url
215 * the URL
216 *
217 * @param method
218 * the HTTP method
219 *
220 * @return the value of the HTTP header this authentication wants set
221 */
222 @Override
223 public String getHeaderValue(URL url, String method) {
224 return getHeaderValueImpl(url.getFile(), method);
225 }
226
227 /**
228 * Reclaculates the request-digest and returns it.
229 *
230 * <P> Used when the requestURI is not the abs_path. The exact
231 * requestURI can be passed as a String.
232 *
233 * @param requestURI
234 * the Request-URI from the HTTP request line
235 *
236 * @param method
237 * the HTTP method
238 *
239 * @return the value of the HTTP header this authentication wants set
240 */
241 String getHeaderValue(String requestURI, String method) {
242 return getHeaderValueImpl(requestURI, method);
243 }
244
245 /**
246 * Check if the header indicates that the current auth. parameters are stale.
247 * If so, then replace the relevant field with the new value
248 * and return true. Otherwise return false.
249 * returning true means the request can be retried with the same userid/password
250 * returning false means we have to go back to the user to ask for a new
251 * username password.
252 */
253 @Override
254 public boolean isAuthorizationStale (String header) {
255 HeaderParser p = new HeaderParser (header);
256 String s = p.findValue ("stale");
257 if (s == null || !s.equals("true"))
258 return false;
259 String newNonce = p.findValue ("nonce");
260 if (newNonce == null || "".equals(newNonce)) {
261 return false;
262 }
263 params.setNonce (newNonce);
264 return true;
265 }
266
267 /**
268 * Set header(s) on the given connection.
269 * @param conn The connection to apply the header(s) to
270 * @param p A source of header values for this connection, if needed.
271 * @param raw Raw header values for this connection, if needed.
272 * @return true if all goes well, false if no headers were set.
273 */
274 @Override
275 public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
276 params.setNonce (p.findValue("nonce"));
277 params.setOpaque (p.findValue("opaque"));
278 params.setQop (p.findValue("qop"));
279
280 String uri="";
281 String method;
282 if (type == PROXY_AUTHENTICATION &&
283 conn.tunnelState() == HttpURLConnection.TunnelState.SETUP) {
284 uri = HttpURLConnection.connectRequestURI(conn.getURL());
285 method = HTTP_CONNECT;
286 } else {
287 try {
288 uri = conn.getRequestURI();
289 } catch (IOException e) {}
290 method = conn.getMethod();
291 }
292
293 if (params.nonce == null || authMethod == null || pw == null || realm == null) {
294 return false;
295 }
296 if (authMethod.length() >= 1) {
297 // Method seems to get converted to all lower case elsewhere.
298 // It really does need to start with an upper case letter
299 // here.
300 authMethod = Character.toUpperCase(authMethod.charAt(0))
301 + authMethod.substring(1).toLowerCase();
302 }
303 String algorithm = p.findValue("algorithm");
304 if (algorithm == null || "".equals(algorithm)) {
305 algorithm = "MD5"; // The default, accoriding to rfc2069
306 }
307 params.setAlgorithm (algorithm);
308
309 // If authQop is true, then the server is doing RFC2617 and
310 // has offered qop=auth. We do not support any other modes
311 // and if auth is not offered we fallback to the RFC2069 behavior
312
313 if (params.authQop()) {
314 params.setNewCnonce();
315 }
316
317 String value = getHeaderValueImpl (uri, method);
318 if (value != null) {
319 conn.setAuthenticationProperty(getHeaderName(), value);
320 return true;
321 } else {
322 return false;
323 }
324 }
325
326 /* Calculate the Authorization header field given the request URI
327 * and based on the authorization information in params
328 */
329 private String getHeaderValueImpl (String uri, String method) {
330 String response;
331 char[] passwd = pw.getPassword();
332 boolean qop = params.authQop();
333 String opaque = params.getOpaque();
334 String cnonce = params.getCnonce ();
335 String nonce = params.getNonce ();
336 String algorithm = params.getAlgorithm ();
337 params.incrementNC ();
338 int nccount = params.getNCCount ();
339 String ncstring=null;
340
341 if (nccount != -1) {
342 ncstring = Integer.toHexString (nccount).toLowerCase();
343 int len = ncstring.length();
344 if (len < 8)
345 ncstring = zeroPad [len] + ncstring;
346 }
347
348 try {
349 response = computeDigest(true, pw.getUserName(),passwd,realm,
350 method, uri, nonce, cnonce, ncstring);
351 } catch (NoSuchAlgorithmException ex) {
352 return null;
353 }
354
355 String ncfield = "\"";
356 if (qop) {
357 ncfield = "\", nc=" + ncstring;
358 }
359
360 String value = authMethod
361 + " username=\"" + pw.getUserName()
362 + "\", realm=\"" + realm
363 + "\", nonce=\"" + nonce
364 + ncfield
365 + ", uri=\"" + uri
366 + "\", response=\"" + response
367 + "\", algorithm=\"" + algorithm;
368 if (opaque != null) {
369 value = value + "\", opaque=\"" + opaque;
370 }
371 if (cnonce != null) {
372 value = value + "\", cnonce=\"" + cnonce;
373 }
374 if (qop) {
375 value = value + "\", qop=\"auth";
376 }
377 value = value + "\"";
378 return value;
379 }
380
381 public void checkResponse (String header, String method, URL url)
382 throws IOException {
383 checkResponse (header, method, url.getFile());
384 }
385
386 public void checkResponse (String header, String method, String uri)
387 throws IOException {
388 char[] passwd = pw.getPassword();
389 String username = pw.getUserName();
390 boolean qop = params.authQop();
391 String opaque = params.getOpaque();
392 String cnonce = params.cnonce;
393 String nonce = params.getNonce ();
394 String algorithm = params.getAlgorithm ();
395 int nccount = params.getNCCount ();
396 String ncstring=null;
397
398 if (header == null) {
399 throw new ProtocolException ("No authentication information in response");
400 }
401
402 if (nccount != -1) {
403 ncstring = Integer.toHexString (nccount).toUpperCase();
404 int len = ncstring.length();
405 if (len < 8)
406 ncstring = zeroPad [len] + ncstring;
407 }
408 try {
409 String expected = computeDigest(false, username,passwd,realm,
410 method, uri, nonce, cnonce, ncstring);
411 HeaderParser p = new HeaderParser (header);
412 String rspauth = p.findValue ("rspauth");
413 if (rspauth == null) {
414 throw new ProtocolException ("No digest in response");
415 }
416 if (!rspauth.equals (expected)) {
417 throw new ProtocolException ("Response digest invalid");
418 }
419 /* Check if there is a nextnonce field */
420 String nextnonce = p.findValue ("nextnonce");
421 if (nextnonce != null && ! "".equals(nextnonce)) {
422 params.setNonce (nextnonce);
423 }
424
425 } catch (NoSuchAlgorithmException ex) {
426 throw new ProtocolException ("Unsupported algorithm in response");
427 }
428 }
429
430 private String computeDigest(
431 boolean isRequest, String userName, char[] password,
432 String realm, String connMethod,
433 String requestURI, String nonceString,
434 String cnonce, String ncValue
435 ) throws NoSuchAlgorithmException
436 {
437
438 String A1, HashA1;
439 String algorithm = params.getAlgorithm ();
440 boolean md5sess = algorithm.equalsIgnoreCase ("MD5-sess");
441
442 MessageDigest md = MessageDigest.getInstance(md5sess?"MD5":algorithm);
443
444 if (md5sess) {
445 if ((HashA1 = params.getCachedHA1 ()) == null) {
446 String s = userName + ":" + realm + ":";
447 String s1 = encode (s, password, md);
448 A1 = s1 + ":" + nonceString + ":" + cnonce;
449 HashA1 = encode(A1, null, md);
450 params.setCachedHA1 (HashA1);
451 }
452 } else {
453 A1 = userName + ":" + realm + ":";
454 HashA1 = encode(A1, password, md);
455 }
456
457 String A2;
458 if (isRequest) {
459 A2 = connMethod + ":" + requestURI;
460 } else {
461 A2 = ":" + requestURI;
462 }
463 String HashA2 = encode(A2, null, md);
464 String combo, finalHash;
465
466 if (params.authQop()) { /* RRC2617 when qop=auth */
467 combo = HashA1+ ":" + nonceString + ":" + ncValue + ":" +
468 cnonce + ":auth:" +HashA2;
469
470 } else { /* for compatibility with RFC2069 */
471 combo = HashA1 + ":" +
472 nonceString + ":" +
473 HashA2;
474 }
475 finalHash = encode(combo, null, md);
476 return finalHash;
477 }
478
479 private final static char charArray[] = {
480 '0', '1', '2', '3', '4', '5', '6', '7',
481 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
482 };
483
484 private final static String zeroPad[] = {
485 // 0 1 2 3 4 5 6 7
486 "00000000", "0000000", "000000", "00000", "0000", "000", "00", "0"
487 };
488
489 private String encode(String src, char[] passwd, MessageDigest md) {
490 try {
491 md.update(src.getBytes("ISO-8859-1"));
492 } catch (java.io.UnsupportedEncodingException uee) {
493 assert false;
494 }
495 if (passwd != null) {
496 byte[] passwdBytes = new byte[passwd.length];
497 for (int i=0; i<passwd.length; i++)
498 passwdBytes[i] = (byte)passwd[i];
499 md.update(passwdBytes);
500 Arrays.fill(passwdBytes, (byte)0x00);
501 }
502 byte[] digest = md.digest();
503
504 StringBuffer res = new StringBuffer(digest.length * 2);
505 for (int i = 0; i < digest.length; i++) {
506 int hashchar = ((digest[i] >>> 4) & 0xf);
507 res.append(charArray[hashchar]);
508 hashchar = (digest[i] & 0xf);
509 res.append(charArray[hashchar]);
510 }
511 return res.toString();
512 }
513}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t