1 /*
2  * Copyright (c) 2001, 2019, 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 import java.io.*;
25 import java.net.*;
26 import java.security.KeyStore;
27 import javax.net.*;
28 import javax.net.ssl.*;
29 
30 import jdk.test.lib.process.OutputAnalyzer;
31 import jdk.test.lib.process.ProcessTools;
32 import jdk.test.lib.net.URIBuilder;
33 
34 /*
35  * @test
36  * @bug 4423074
37  * @modules java.base/sun.net.www
38  * @summary This test case is written to test the https POST through a proxy
39  *          with proxy authentication. It includes a simple server that serves
40  *          http POST method requests in secure channel, and a client that
41  *          makes https POST request through a proxy.
42  * @library /test/lib
43  * @build jdk.test.lib.Utils
44  *        jdk.test.lib.Asserts
45  *        jdk.test.lib.JDKToolFinder
46  *        jdk.test.lib.JDKToolLauncher
47  *        jdk.test.lib.Platform
48  *        jdk.test.lib.process.*
49  * @compile OriginServer.java ProxyTunnelServer.java
50  * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= PostThruProxyWithAuth
51  * @run main/othervm -Djava.net.preferIPv6Addresses=true
52                      -Djdk.http.auth.tunneling.disabledSchemes= PostThruProxyWithAuth
53  */
54 public class PostThruProxyWithAuth {
55 
56     private static final String TEST_SRC = System.getProperty("test.src", ".");
57     private static final int TIMEOUT = 30000;
58 
59     /*
60      * Where do we find the keystores?
61      */
62     static String pathToStores = "../../../../../../javax/net/ssl/etc";
63     static String keyStoreFile = "keystore";
64     static String trustStoreFile = "truststore";
65     static String passwd = "passphrase";
66 
67     volatile private static int serverPort = 0;
68     private static ProxyTunnelServer pserver;
69     private static TestServer server;
70 
71     static final String RESPONSE_MSG =
72         "Https POST thru proxy is successful with proxy authentication";
73 
74     /*
75      * The TestServer implements a OriginServer that
76      * processes HTTP requests and responses.
77      */
78     static class TestServer extends OriginServer {
TestServer(ServerSocket ss)79         public TestServer(ServerSocket ss) throws Exception {
80             super(ss);
81         }
82 
83         /*
84          * Returns an array of bytes containing the bytes for
85          * the data sent in the response.
86          *
87          * @return bytes for the data in the response
88          */
getBytes()89         public byte[] getBytes() {
90             return RESPONSE_MSG.getBytes();
91         }
92     }
93 
94     /*
95      * Main method to create the server and client
96      */
main(String args[])97     public static void main(String args[]) throws Exception {
98         String keyFilename = TEST_SRC + "/" + pathToStores + "/" + keyStoreFile;
99         String trustFilename = TEST_SRC + "/" + pathToStores + "/"
100                 + trustStoreFile;
101 
102         System.setProperty("javax.net.ssl.keyStore", keyFilename);
103         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
104         System.setProperty("javax.net.ssl.trustStore", trustFilename);
105         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
106 
107         boolean useSSL = true;
108         /*
109          * setup the server
110          */
111         try {
112             InetAddress localhost = InetAddress.getLocalHost();
113             ServerSocketFactory ssf = getServerSocketFactory(useSSL);
114             ServerSocket ss = ssf.createServerSocket(serverPort, 0, localhost);
115             ss.setSoTimeout(TIMEOUT);  // 30 seconds
116             serverPort = ss.getLocalPort();
117             server = new TestServer(ss);
118             System.out.println("Server started at: " + ss);
119         } catch (Exception e) {
120             System.out.println("Server side failed:" +
121                                 e.getMessage());
122             throw e;
123         }
124         // trigger the client
125         try {
126             doClientSide();
127         } catch (Exception e) {
128             System.out.println("Client side failed: " +
129                                 e.getMessage());
130             throw e;
131         }
132         long connectCount = pserver.getConnectCount();
133         if (connectCount == 0) {
134             throw new AssertionError("Proxy was not used!");
135         } else {
136             System.out.println("Proxy CONNECT count: " + connectCount);
137         }
138     }
139 
getServerSocketFactory(boolean useSSL)140     private static ServerSocketFactory getServerSocketFactory
141                    (boolean useSSL) throws Exception {
142         if (useSSL) {
143             // set up key manager to do server authentication
144             SSLContext ctx = SSLContext.getInstance("TLS");
145             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
146             KeyStore ks = KeyStore.getInstance("JKS");
147             char[] passphrase = passwd.toCharArray();
148 
149             ks.load(new FileInputStream(System.getProperty(
150                         "javax.net.ssl.keyStore")), passphrase);
151             kmf.init(ks, passphrase);
152             ctx.init(kmf.getKeyManagers(), null, null);
153 
154             return ctx.getServerSocketFactory();
155         } else {
156             return ServerSocketFactory.getDefault();
157         }
158     }
159 
160     /*
161      * Message to be posted
162      */
163     static String postMsg = "Testing HTTP post on a https server";
164 
doClientSide()165     static void doClientSide() throws Exception {
166         /*
167          * setup up a proxy
168          */
169         SocketAddress pAddr = setupProxy();
170 
171         /*
172          * we want to avoid URLspoofCheck failures in cases where the cert
173          * DN name does not match the hostname in the URL.
174          */
175         HttpsURLConnection.setDefaultHostnameVerifier(
176                                       new NameVerifier());
177 
178         URL url = URIBuilder.newBuilder()
179                       .scheme("https")
180                       .host(getHostname())
181                       .port(serverPort)
182                       .toURL();
183 
184         Proxy p = new Proxy(Proxy.Type.HTTP, pAddr);
185         System.out.println("Client connecting to: " + url);
186         System.out.println("Through proxy: " + pAddr);
187         HttpsURLConnection https = (HttpsURLConnection)url.openConnection(p);
188         https.setConnectTimeout(TIMEOUT);
189         https.setReadTimeout(TIMEOUT);
190         https.setDoOutput(true);
191         https.setRequestMethod("POST");
192         PrintStream ps = null;
193         try {
194            ps = new PrintStream(https.getOutputStream());
195            ps.println(postMsg);
196            ps.flush();
197            if (https.getResponseCode() != 200) {
198                 throw new RuntimeException("test Failed");
199            }
200            ps.close();
201            // clear the pipe
202            BufferedReader in = new BufferedReader(
203                                 new InputStreamReader(
204                                 https.getInputStream()));
205            String inputLine;
206            boolean msgFound = false;
207            while ((inputLine = in.readLine()) != null) {
208                 System.out.println("Client received: " + inputLine);
209                 if (inputLine.contains(RESPONSE_MSG)) msgFound = true;
210            }
211            in.close();
212            if (!msgFound) {
213                throw new RuntimeException("POST message not found.");
214            }
215         } catch (SSLException e) {
216             if (ps != null)
217                 ps.close();
218             throw e;
219         } catch (SocketTimeoutException e) {
220             System.out.println("Client can not get response in time: "
221                     + e.getMessage());
222         }
223     }
224 
225     static class NameVerifier implements HostnameVerifier {
verify(String hostname, SSLSession session)226         public boolean verify(String hostname, SSLSession session) {
227             return true;
228         }
229     }
230 
setupProxy()231     static SocketAddress setupProxy() throws IOException {
232 
233         InetAddress localhost = InetAddress.getLocalHost();
234         pserver = new ProxyTunnelServer(localhost);
235 
236         /*
237          * register a system wide authenticator and setup the proxy for
238          * authentication
239          */
240         Authenticator.setDefault(new TestAuthenticator());
241 
242         // register with the username and password
243         pserver.needUserAuth(true);
244         pserver.setUserAuth("Test", "test123");
245 
246         pserver.start();
247 
248         return new InetSocketAddress(localhost, pserver.getPort());
249     }
250 
251     public static class TestAuthenticator extends Authenticator {
getPasswordAuthentication()252         public PasswordAuthentication getPasswordAuthentication() {
253             return new PasswordAuthentication("Test",
254                                          "test123".toCharArray());
255         }
256     }
257 
getHostname()258     private static String getHostname() {
259         try {
260             OutputAnalyzer oa = ProcessTools.executeCommand("hostname");
261             return oa.getOutput().trim();
262         } catch (Throwable e) {
263             throw new RuntimeException("Get hostname failed.", e);
264         }
265     }
266 }
267