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