1 /*
2  * Copyright (c) 2016, 2018, 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.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.net.URI;
29 import java.net.URISyntaxException;
30 import java.security.Security;
31 import java.net.http.HttpClient;
32 import java.net.http.HttpRequest;
33 import java.net.http.HttpRequest.BodyPublishers;
34 import java.net.http.HttpResponse.BodyHandlers;
35 import javax.net.ssl.SSLContext;
36 import javax.net.ssl.SSLParameters;
37 import javax.net.ssl.SSLSession;
38 
39 /*
40  * @test
41  * @bug 8150769 8157107
42  * @library server
43  * @summary Checks that SSL parameters can be set for HTTP/2 connection
44  * @modules java.base/sun.net.www.http
45  *          java.net.http/jdk.internal.net.http.common
46  *          java.net.http/jdk.internal.net.http.frame
47  *          java.net.http/jdk.internal.net.http.hpack
48  * @run main/othervm
49  *       -Djdk.internal.httpclient.debug=true
50  *       -Djdk.httpclient.HttpClient.log=all
51  *       TLSConnection
52  */
53 public class TLSConnection {
54 
55     private static final String KEYSTORE = System.getProperty("test.src")
56             + File.separator + "keystore.p12";
57     private static final String PASSWORD = "password";
58 
59     private static final SSLParameters USE_DEFAULT_SSL_PARAMETERS = new SSLParameters();
60 
61     // expect highest supported version we know about
expectedTLSVersion(SSLContext ctx)62     static String expectedTLSVersion(SSLContext ctx) throws Exception {
63         if (ctx == null)
64             ctx = SSLContext.getDefault();
65         SSLParameters params = ctx.getSupportedSSLParameters();
66         String[] protocols = params.getProtocols();
67         for (String prot : protocols) {
68             if (prot.equals("TLSv1.3"))
69                 return "TLSv1.3";
70         }
71         return "TLSv1.2";
72     }
73 
main(String[] args)74     public static void main(String[] args) throws Exception {
75         // re-enable 3DES
76         Security.setProperty("jdk.tls.disabledAlgorithms", "");
77 
78         // enable all logging
79         System.setProperty("jdk.httpclient.HttpClient.log", "all,frames:all");
80 
81         // initialize JSSE
82         System.setProperty("javax.net.ssl.keyStore", KEYSTORE);
83         System.setProperty("javax.net.ssl.keyStorePassword", PASSWORD);
84         System.setProperty("javax.net.ssl.trustStore", KEYSTORE);
85         System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
86 
87         Handler handler = new Handler();
88 
89         try (Http2TestServer server = new Http2TestServer("localhost", true, 0)) {
90             server.addHandler(handler, "/");
91             server.start();
92 
93             int port = server.getAddress().getPort();
94             String uriString = "https://localhost:" + Integer.toString(port);
95 
96             // run test cases
97             boolean success = true;
98 
99             SSLParameters parameters = null;
100             success &= expectFailure(
101                     "---\nTest #1: SSL parameters is null, expect NPE",
102                     () -> connect(uriString, parameters),
103                     NullPointerException.class);
104 
105             success &= expectSuccess(
106                     "---\nTest #2: default SSL parameters, "
107                             + "expect successful connection",
108                     () -> connect(uriString, USE_DEFAULT_SSL_PARAMETERS));
109             success &= checkProtocol(handler.getSSLSession(), expectedTLSVersion(null));
110 
111             // set SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite
112             // which has less priority in default cipher suite list
113             success &= expectSuccess(
114                     "---\nTest #3: SSL parameters with "
115                             + "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite, "
116                             + "expect successful connection",
117                     () -> connect(uriString, new SSLParameters(
118                             new String[] { "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA" },
119                             new String[] { "TLSv1.2" })));
120             success &= checkProtocol(handler.getSSLSession(), "TLSv1.2");
121             success &= checkCipherSuite(handler.getSSLSession(),
122                     "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
123 
124             // set TLS_RSA_WITH_AES_128_CBC_SHA cipher suite
125             // which has less priority in default cipher suite list
126             // also set TLSv1.2 protocol
127             success &= expectSuccess(
128                     "---\nTest #4: SSL parameters with "
129                             + "TLS_RSA_WITH_AES_128_CBC_SHA cipher suite,"
130                             + " expect successful connection",
131                     () -> connect(uriString, new SSLParameters(
132                             new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA" },
133                             new String[] { "TLSv1.2" })));
134             success &= checkProtocol(handler.getSSLSession(), "TLSv1.2");
135             success &= checkCipherSuite(handler.getSSLSession(),
136                     "TLS_RSA_WITH_AES_128_CBC_SHA");
137 
138             if (success) {
139                 System.out.println("Test passed");
140             } else {
141                 throw new RuntimeException("At least one test case failed");
142             }
143         }
144     }
145 
146     private static interface Test {
147 
run()148         public void run() throws Exception;
149     }
150 
151     private static class Handler implements Http2Handler {
152 
153         private static final byte[] BODY = "Test response".getBytes();
154 
155         private volatile SSLSession sslSession;
156 
157         @Override
handle(Http2TestExchange t)158         public void handle(Http2TestExchange t) throws IOException {
159             System.out.println("Handler: received request to "
160                     + t.getRequestURI());
161 
162             try (InputStream is = t.getRequestBody()) {
163                 byte[] body = is.readAllBytes();
164                 System.out.println("Handler: read " + body.length
165                         + " bytes of body: ");
166                 System.out.println(new String(body));
167             }
168 
169             try (OutputStream os = t.getResponseBody()) {
170                 t.sendResponseHeaders(200, BODY.length);
171                 os.write(BODY);
172             }
173 
174             sslSession = t.getSSLSession();
175         }
176 
getSSLSession()177         SSLSession getSSLSession() {
178             return sslSession;
179         }
180     }
181 
connect(String uriString, SSLParameters sslParameters)182     private static void connect(String uriString, SSLParameters sslParameters)
183             throws URISyntaxException, IOException, InterruptedException
184     {
185         HttpClient.Builder builder = HttpClient.newBuilder()
186                 .version(HttpClient.Version.HTTP_2);
187         if (sslParameters != USE_DEFAULT_SSL_PARAMETERS)
188             builder.sslParameters(sslParameters);
189         HttpClient client = builder.build();
190 
191         HttpRequest request = HttpRequest.newBuilder(new URI(uriString))
192                 .POST(BodyPublishers.ofString("body"))
193                 .build();
194         String body = client.send(request, BodyHandlers.ofString()).body();
195 
196         System.out.println("Response: " + body);
197     }
198 
checkProtocol(SSLSession session, String protocol)199     private static boolean checkProtocol(SSLSession session, String protocol) {
200         if (session == null) {
201             System.out.println("Check protocol: no session provided");
202             return false;
203         }
204 
205         System.out.println("Check protocol: negotiated protocol: "
206                 + session.getProtocol());
207         System.out.println("Check protocol: expected protocol: "
208                 + protocol);
209         if (!protocol.equals(session.getProtocol())) {
210             System.out.println("Check protocol: unexpected negotiated protocol");
211             return false;
212         }
213 
214         return true;
215     }
216 
checkCipherSuite(SSLSession session, String ciphersuite)217     private static boolean checkCipherSuite(SSLSession session, String ciphersuite) {
218         if (session == null) {
219             System.out.println("Check protocol: no session provided");
220             return false;
221         }
222 
223         System.out.println("Check protocol: negotiated ciphersuite: "
224                 + session.getCipherSuite());
225         System.out.println("Check protocol: expected ciphersuite: "
226                 + ciphersuite);
227         if (!ciphersuite.equals(session.getCipherSuite())) {
228             System.out.println("Check protocol: unexpected negotiated ciphersuite");
229             return false;
230         }
231 
232         return true;
233     }
234 
expectSuccess(String message, Test test)235     private static boolean expectSuccess(String message, Test test) {
236         System.out.println(message);
237         try {
238             test.run();
239             System.out.println("Passed");
240             return true;
241         } catch (Exception e) {
242             System.out.println("Failed: unexpected exception:");
243             e.printStackTrace(System.out);
244             return false;
245         }
246     }
247 
expectFailure(String message, Test test, Class<? extends Throwable> expectedException)248     private static boolean expectFailure(String message, Test test,
249                                          Class<? extends Throwable> expectedException) {
250 
251         System.out.println(message);
252         try {
253             test.run();
254             System.out.println("Failed: unexpected successful connection");
255             return false;
256         } catch (Exception e) {
257             System.out.println("Got an exception:");
258             e.printStackTrace(System.out);
259             if (expectedException != null
260                     && !expectedException.isAssignableFrom(e.getClass())) {
261                 System.out.printf("Failed: expected %s, but got %s%n",
262                         expectedException.getName(),
263                         e.getClass().getName());
264                 return false;
265             }
266             System.out.println("Passed: expected exception");
267             return true;
268         }
269     }
270 }
271