1 /* 2 * Copyright (c) 2001, 2010, 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 4432213 27 * @run main/othervm -Dhttp.auth.digest.validateServer=true DigestTest 28 * @summary Need to support Digest Authentication for Proxies 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 class DigestServer extends Thread { 44 45 ServerSocket s; 46 Socket s1; 47 InputStream is; 48 OutputStream os; 49 int port; 50 51 String reply1 = "HTTP/1.1 401 Unauthorized\r\n"+ 52 "WWW-Authenticate: Digest realm=\""+realm+"\" domain=/ "+ 53 "nonce=\""+nonce+"\" qop=\"auth\"\r\n\r\n"; 54 55 String reply2 = "HTTP/1.1 200 OK\r\n" + 56 "Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" + 57 "Server: Apache/1.3.14 (Unix)\r\n" + 58 "Content-Type: text/html; charset=iso-8859-1\r\n" + 59 "Transfer-encoding: chunked\r\n\r\n"+ 60 "B\r\nHelloWorld1\r\n"+ 61 "B\r\nHelloWorld2\r\n"+ 62 "B\r\nHelloWorld3\r\n"+ 63 "B\r\nHelloWorld4\r\n"+ 64 "B\r\nHelloWorld5\r\n"+ 65 "0\r\n"+ 66 "Authentication-Info: "; 67 68 DigestServer (ServerSocket y) { 69 s = y; 70 port = s.getLocalPort(); 71 } 72 73 public void run () { 74 try { 75 s1 = s.accept (); 76 is = s1.getInputStream (); 77 os = s1.getOutputStream (); 78 is.read (); 79 os.write (reply1.getBytes()); 80 Thread.sleep (2000); 81 s1.close (); 82 83 s1 = s.accept (); 84 is = s1.getInputStream (); 85 os = s1.getOutputStream (); 86 is.read (); 87 // need to get the cnonce out of the response 88 MessageHeader header = new MessageHeader (is); 89 String raw = header.findValue ("Authorization"); 90 HeaderParser parser = new HeaderParser (raw); 91 String cnonce = parser.findValue ("cnonce"); 92 String cnstring = parser.findValue ("nc"); 93 94 String reply = reply2 + getAuthorization (uri, "GET", cnonce, cnstring) +"\r\n"; 95 os.write (reply.getBytes()); 96 Thread.sleep (2000); 97 s1.close (); 98 } catch (Exception e) { 99 System.out.println (e); 100 e.printStackTrace(); 101 } finally { 102 try { s.close(); } catch (IOException unused) {} 103 } 104 } 105 106 static char[] passwd = "password".toCharArray(); 107 static String username = "user"; 108 static String nonce = "abcdefghijklmnopqrstuvwxyz"; 109 static String realm = "wallyworld"; 110 static String uri = "/foo.html"; 111 112 private String getAuthorization (String uri, String method, String cnonce, String cnstring) { 113 String response; 114 115 try { 116 response = computeDigest(false, username,passwd,realm, 117 method, uri, nonce, cnonce, cnstring); 118 } catch (NoSuchAlgorithmException ex) { 119 return null; 120 } 121 122 String value = "Digest" 123 + " qop=auth\"" 124 + "\", cnonce=\"" + cnonce 125 + "\", rspauth=\"" + response 126 + "\", nc=\"" + cnstring + "\""; 127 return (value+ "\r\n"); 128 } 129 130 private String computeDigest( 131 boolean isRequest, String userName, char[] password, 132 String realm, String connMethod, 133 String requestURI, String nonceString, 134 String cnonce, String ncValue 135 ) throws NoSuchAlgorithmException 136 { 137 138 String A1, HashA1; 139 140 MessageDigest md = MessageDigest.getInstance("MD5"); 141 142 { 143 A1 = userName + ":" + realm + ":"; 144 HashA1 = encode(A1, password, md); 145 } 146 147 String A2; 148 if (isRequest) { 149 A2 = connMethod + ":" + requestURI; 150 } else { 151 A2 = ":" + requestURI; 152 } 153 String HashA2 = encode(A2, null, md); 154 String combo, finalHash; 155 156 { /* RRC2617 when qop=auth */ 157 combo = HashA1+ ":" + nonceString + ":" + ncValue + ":" + 158 cnonce + ":auth:" +HashA2; 159 160 } 161 finalHash = encode(combo, null, md); 162 return finalHash; 163 } 164 165 private final static char charArray[] = { 166 '0', '1', '2', '3', '4', '5', '6', '7', 167 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 168 }; 169 170 private String encode(String src, char[] passwd, MessageDigest md) { 171 md.update(src.getBytes()); 172 if (passwd != null) { 173 byte[] passwdBytes = new byte[passwd.length]; 174 for (int i=0; i<passwd.length; i++) 175 passwdBytes[i] = (byte)passwd[i]; 176 md.update(passwdBytes); 177 Arrays.fill(passwdBytes, (byte)0x00); 178 } 179 byte[] digest = md.digest(); 180 181 StringBuffer res = new StringBuffer(digest.length * 2); 182 for (int i = 0; i < digest.length; i++) { 183 int hashchar = ((digest[i] >>> 4) & 0xf); 184 res.append(charArray[hashchar]); 185 hashchar = (digest[i] & 0xf); 186 res.append(charArray[hashchar]); 187 } 188 return res.toString(); 189 } 190 191 } 192 193 public class DigestTest { 194 195 static class MyAuthenticator extends Authenticator { 196 public MyAuthenticator () { 197 super (); 198 } 199 200 public PasswordAuthentication getPasswordAuthentication () 201 { 202 return (new PasswordAuthentication ("user", "Wrongpassword".toCharArray())); 203 } 204 } 205 206 207 public static void main(String[] args) throws Exception { 208 int port; 209 DigestServer server; 210 ServerSocket sock; 211 212 try { 213 sock = new ServerSocket (0); 214 port = sock.getLocalPort (); 215 } 216 catch (Exception e) { 217 System.out.println ("Exception: " + e); 218 return; 219 } 220 221 server = new DigestServer(sock); 222 server.start (); 223 boolean passed = false; 224 225 try { 226 Authenticator.setDefault (new MyAuthenticator ()); 227 String s = "http://localhost:" + port + DigestServer.uri; 228 URL url = new URL(s); 229 java.net.URLConnection conURL = url.openConnection(); 230 231 InputStream in = conURL.getInputStream(); 232 while (in.read () != -1) {} 233 in.close (); 234 } catch(ProtocolException e) { 235 passed = true; 236 } 237 238 if (!passed) { 239 throw new RuntimeException ("Expected a ProtocolException from wrong password"); 240 } 241 } 242 }