1 /* 2 * Copyright (c) 2001, 2009, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 6870935 27 * @modules java.base/sun.net.www 28 * @run main/othervm -Dhttp.nonProxyHosts="" -Dhttp.auth.digest.validateProxy=true B6870935 29 */ 30 31 import java.io.*; 32 import java.util.*; 33 import java.net.*; 34 import java.security.*; 35 import sun.net.www.*; 36 37 /* This is one simple test of the RFC2617 digest authentication behavior 38 * It specifically tests that the client correctly checks the returned 39 * Authentication-Info header field from the server and throws an exception 40 * if the password is wrong 41 */ 42 43 public class B6870935 { 44 45 static char[] passwd = "password".toCharArray(); 46 static String username = "user"; 47 static String nonce = "abcdefghijklmnopqrstuvwxyz"; 48 static String realm = "wallyworld"; 49 static String uri = "http://www.ibm.com"; 50 static volatile boolean error = false; 51 52 static class DigestServer extends Thread { 53 54 ServerSocket s; 55 InputStream is; 56 OutputStream os; 57 int port; 58 59 String reply1 = "HTTP/1.1 407 Proxy Authentication Required\r\n"+ 60 "Proxy-Authenticate: Digest realm=\""+realm+"\" domain=/ "+ 61 "nonce=\""+nonce+"\" qop=\"auth\"\r\n\r\n"; 62 63 String reply2 = "HTTP/1.1 200 OK\r\n" + 64 "Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" + 65 "Server: Apache/1.3.14 (Unix)\r\n" + 66 "Content-Type: text/html; charset=iso-8859-1\r\n" + 67 "Transfer-encoding: chunked\r\n\r\n"+ 68 "B\r\nHelloWorld1\r\n"+ 69 "B\r\nHelloWorld2\r\n"+ 70 "B\r\nHelloWorld3\r\n"+ 71 "B\r\nHelloWorld4\r\n"+ 72 "B\r\nHelloWorld5\r\n"+ 73 "0\r\n"+ 74 "Proxy-Authentication-Info: "; 75 76 DigestServer (ServerSocket y) { 77 s = y; 78 port = s.getLocalPort(); 79 } 80 81 public void run () { 82 try { 83 Socket s1 = s.accept (); 84 is = s1.getInputStream (); 85 os = s1.getOutputStream (); 86 is.read (); 87 os.write (reply1.getBytes()); 88 Thread.sleep (2000); 89 s1.close (); 90 91 s1 = s.accept (); 92 is = s1.getInputStream (); 93 os = s1.getOutputStream (); 94 is.read (); 95 // need to get the cnonce out of the response 96 MessageHeader header = new MessageHeader (is); 97 String raw = header.findValue ("Proxy-Authorization"); 98 HeaderParser parser = new HeaderParser (raw); 99 String cnonce = parser.findValue ("cnonce"); 100 String cnstring = parser.findValue ("nc"); 101 String clientrsp = parser.findValue ("response"); 102 String expected = computeDigest( 103 true, username,passwd,realm, 104 "GET", uri, nonce, cnonce, cnstring 105 ); 106 if (!expected.equals(clientrsp)) { 107 s1.close (); 108 s.close (); 109 error = true; 110 return; 111 } 112 113 String reply = reply2 + getAuthorization ( 114 realm, false, uri, "GET", cnonce, 115 cnstring, passwd, username 116 ) +"\r\n"; 117 os.write (reply.getBytes()); 118 Thread.sleep (2000); 119 s1.close (); 120 } 121 catch (Exception e) { 122 System.out.println (e); 123 e.printStackTrace(); 124 } 125 } 126 127 private String getAuthorization (String realm, boolean isRequest, String uri, String method, String cnonce, String cnstring, char[] password, String username) { 128 String response; 129 130 try { 131 response = computeDigest(isRequest, username,passwd,realm, 132 method, uri, nonce, cnonce, cnstring); 133 } catch (NoSuchAlgorithmException ex) { 134 return null; 135 } 136 137 String value = "Digest" 138 + " qop=\"auth" 139 + "\", cnonce=\"" + cnonce 140 + "\", rspauth=\"" + response 141 + "\", nc=\"" + cnstring + "\""; 142 return (value+ "\r\n"); 143 } 144 145 private String computeDigest( 146 boolean isRequest, String userName, char[] password, 147 String realm, String connMethod, 148 String requestURI, String nonceString, 149 String cnonce, String ncValue 150 ) throws NoSuchAlgorithmException 151 { 152 153 String A1, HashA1; 154 155 MessageDigest md = MessageDigest.getInstance("MD5"); 156 157 { 158 A1 = userName + ":" + realm + ":"; 159 HashA1 = encode(A1, password, md); 160 } 161 162 String A2; 163 if (isRequest) { 164 A2 = connMethod + ":" + requestURI; 165 } else { 166 A2 = ":" + requestURI; 167 } 168 String HashA2 = encode(A2, null, md); 169 String combo, finalHash; 170 171 { /* RRC2617 when qop=auth */ 172 combo = HashA1+ ":" + nonceString + ":" + ncValue + ":" + 173 cnonce + ":auth:" +HashA2; 174 175 } 176 finalHash = encode(combo, null, md); 177 return finalHash; 178 } 179 180 private final static char charArray[] = { 181 '0', '1', '2', '3', '4', '5', '6', '7', 182 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 183 }; 184 185 private String encode(String src, char[] passwd, MessageDigest md) { 186 md.update(src.getBytes()); 187 if (passwd != null) { 188 byte[] passwdBytes = new byte[passwd.length]; 189 for (int i=0; i<passwd.length; i++) 190 passwdBytes[i] = (byte)passwd[i]; 191 md.update(passwdBytes); 192 Arrays.fill(passwdBytes, (byte)0x00); 193 } 194 byte[] digest = md.digest(); 195 196 StringBuffer res = new StringBuffer(digest.length * 2); 197 for (int i = 0; i < digest.length; i++) { 198 int hashchar = ((digest[i] >>> 4) & 0xf); 199 res.append(charArray[hashchar]); 200 hashchar = (digest[i] & 0xf); 201 res.append(charArray[hashchar]); 202 } 203 return res.toString(); 204 } 205 } 206 207 208 static class MyAuthenticator extends Authenticator { 209 public MyAuthenticator () { 210 super (); 211 } 212 213 public PasswordAuthentication getPasswordAuthentication () 214 { 215 return (new PasswordAuthentication (username, passwd)); 216 } 217 } 218 219 220 public static void main(String[] args) throws Exception { 221 int nLoops = 1; 222 int nSize = 10; 223 int port, n =0; 224 byte b[] = new byte[nSize]; 225 DigestServer server; 226 ServerSocket sock; 227 228 try { 229 sock = new ServerSocket (0); 230 port = sock.getLocalPort (); 231 } 232 catch (Exception e) { 233 System.out.println ("Exception: " + e); 234 return; 235 } 236 237 server = new DigestServer(sock); 238 server.start (); 239 240 try { 241 242 Authenticator.setDefault (new MyAuthenticator ()); 243 SocketAddress addr = new InetSocketAddress ("127.0.0.1", port); 244 Proxy proxy = new Proxy (Proxy.Type.HTTP, addr); 245 String s = "http://www.ibm.com"; 246 URL url = new URL(s); 247 java.net.URLConnection conURL = url.openConnection(proxy); 248 249 InputStream in = conURL.getInputStream(); 250 int c; 251 while ((c = in.read ()) != -1) { 252 } 253 in.close (); 254 } 255 catch(IOException e) { 256 e.printStackTrace(); 257 error = true; 258 } 259 if (error) { 260 throw new RuntimeException ("Error in test"); 261 } 262 } 263 }