1 /*
   2  * Copyright (c) 2001, 2016, 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 4328195
  27  * @summary Need to include the alternate subject DN for certs,
  28  *          https should check for this
  29  * @run main/othervm ServerIdentityTest dnsstore
  30  * @run main/othervm ServerIdentityTest ipstore
  31  *
  32  *     SunJSSE does not support dynamic system properties, no way to re-use
  33  *     system properties in samevm/agentvm mode.
  34  *
  35  * @author Yingxian Wang
  36  */
  37 
  38 import java.io.*;
  39 import java.net.*;
  40 import javax.net.ssl.*;
  41 import java.security.KeyStore;
  42 
  43 public class ServerIdentityTest {
  44 
  45     /*
  46      * =============================================================
  47      * Set the various variables needed for the tests, then
  48      * specify what tests to run on each side.
  49      */
  50 
  51     /*
  52      * Should we run the client or server in a separate thread?
  53      * Both sides can throw exceptions, but do you have a preference
  54      * as to which side should be the main thread.
  55      */
  56     static final boolean separateServerThread = true;
  57 
  58     /*
  59      * Where do we find the keystores?
  60      */
  61     static final String passwd = "changeit";
  62 
  63     /*
  64      * Turn on SSL debugging?
  65      */
  66     static final boolean debug = true;
  67 
  68     static final int FREE_PORT = 0;
  69 
  70     /*
  71      * Is the server ready to serve?
  72      */
  73     volatile boolean serverReady = false;
  74     volatile int serverPort = 0;
  75     volatile Exception serverException = null;
  76     volatile Exception clientException = null;
  77     SSLContext context;
  78     boolean iphost = false;
  79 
  80 
  81     Thread clientThread = null;
  82     Thread serverThread = null;
  83 
  84     /*
  85      * If the client or server is doing some kind of object creation
  86      * that the other side depends on, and that thread prematurely
  87      * exits, you may experience a hang.  The test harness will
  88      * terminate all hung threads after its timeout has expired,
  89      * currently 3 minutes by default, but you might try to be
  90      * smart about it....
  91      */
  92 
  93     /*
  94      * Define the server side of the test.
  95      *
  96      * If the server prematurely exits, serverReady will be set to true
  97      * to avoid infinite hangs.
  98      */
  99     void doServerSide() throws Exception {
 100         SSLServerSocketFactory sslssf = context.getServerSocketFactory();
 101         try (SSLServerSocket sslServerSocket =
 102                 (SSLServerSocket) sslssf.createServerSocket(FREE_PORT)) {
 103 
 104             serverPort = sslServerSocket.getLocalPort();
 105 
 106             /*
 107             * Signal Client, we're ready for his connect.
 108             */
 109             serverReady = true;
 110 
 111             try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept()) {
 112                 BufferedWriter bw = new BufferedWriter(
 113                         new OutputStreamWriter(sslSocket.getOutputStream()));
 114                 bw.write("HTTP/1.1 200 OK\r\n\r\n\r\n");
 115                 bw.flush();
 116                 Thread.sleep(2000);
 117                 sslSocket.getSession().invalidate();
 118             }
 119         }
 120     }
 121 
 122     /*
 123      * Define the client side of the test.
 124      *
 125      * If the server prematurely exits, serverReady will be set to true
 126      * to avoid infinite hangs.
 127      */
 128     void doClientSide() throws Exception {
 129         /*
 130          * Wait for server to get started.
 131          */
 132         while (!serverReady) {
 133             Thread.sleep(50);
 134         }
 135 
 136         String host = (iphost) ? "127.0.0.1" : "localhost";
 137         URL url = new URL("https://" +host + ":" + serverPort + "/index.html");
 138 
 139         HttpURLConnection urlc = (HttpURLConnection)url.openConnection();
 140         try (InputStream is = urlc.getInputStream()) {
 141             is.readAllBytes();
 142         }
 143     }
 144 
 145     /*
 146      * =============================================================
 147      * The remainder is just support stuff
 148      */
 149 
 150     public static void main(String[] args) throws Exception {
 151         String keyFilename = System.getProperty("test.src", ".")
 152                 + "/" + args[0];
 153         String trustFilename = System.getProperty("test.src", ".")
 154                 + "/" + args[0];
 155 
 156         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 157         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 158         System.setProperty("javax.net.ssl.trustStore", trustFilename);
 159         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 160 
 161         if (debug) {
 162             System.setProperty("javax.net.debug", "all");
 163         }
 164 
 165         SSLContext context = SSLContext.getInstance("SSL");
 166 
 167         KeyManager[] kms = new KeyManager[1];
 168         KeyStore ks = KeyStore.getInstance("JKS");
 169         try (FileInputStream fis = new FileInputStream(keyFilename)) {
 170             ks.load(fis, passwd.toCharArray());
 171         }
 172         KeyManager km = new MyKeyManager(ks, passwd.toCharArray());
 173         kms[0] = km;
 174         context.init(kms, null, null);
 175         HttpsURLConnection.setDefaultSSLSocketFactory(
 176                 context.getSocketFactory());
 177 
 178         /*
 179          * Start the tests.
 180          */
 181         System.out.println("Testing " + keyFilename);
 182         new ServerIdentityTest(context, args[0]);
 183     }
 184 
 185     /*
 186      * Primary constructor, used to drive remainder of the test.
 187      *
 188      * Fork off the other side, then do your work.
 189      */
 190     ServerIdentityTest(SSLContext context, String keystore) throws Exception {
 191         this.context = context;
 192         iphost = keystore.equals("ipstore");
 193 
 194         if (separateServerThread) {
 195             startServer(true);
 196             startClient(false);
 197         } else {
 198             startClient(true);
 199             startServer(false);
 200         }
 201 
 202         /*
 203          * Wait for other side to close down.
 204          */
 205         if (separateServerThread) {
 206             serverThread.join();
 207         } else {
 208             clientThread.join();
 209         }
 210 
 211         if (serverException != null || clientException != null) {
 212             throw new RuntimeException("Test failed");
 213         }
 214     }
 215 
 216     void startServer(boolean newThread) throws Exception {
 217         if (newThread) {
 218             serverThread = new Thread() {
 219                 public void run() {
 220                     try {
 221                         doServerSide();
 222                     } catch (Exception e) {
 223                         /*
 224                          * Our server thread just died.
 225                          *
 226                          * Release the client, if not active already...
 227                          */
 228                         System.err.println("Server died...");
 229                         e.printStackTrace(System.out);
 230                         serverReady = true;
 231                         serverException = e;
 232                     }
 233                 }
 234             };
 235             serverThread.start();
 236         } else {
 237             doServerSide();
 238         }
 239     }
 240 
 241     void startClient(boolean newThread) throws Exception {
 242         if (newThread) {
 243             clientThread = new Thread() {
 244                 public void run() {
 245                     try {
 246                         doClientSide();
 247                     } catch (Exception e) {
 248                         /*
 249                          * Our client thread just died.
 250                          */
 251                         System.err.println("Client died...");
 252                         e.printStackTrace(System.out);
 253                         clientException = e;
 254                     }
 255                 }
 256             };
 257             clientThread.start();
 258         } else {
 259             doClientSide();
 260         }
 261     }
 262 }