test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java
author xuelei
Mon, 25 Jun 2018 13:41:39 -0700
changeset 50768 68fa3d4026ea
parent 47216 71c04702a3d5
child 51031 a40b75d39ecd
permissions -rw-r--r--
8196584: TLS 1.3 Implementation Reviewed-by: ascarpino, coffeys, dfuchs, jjiang, jnimeh, mullan, rhalade, ssahoo, valeriep, weijun, wetmore, xuelei Contributed-by: Adam Petcher <adam.petcher@oracle.com>, Amanda Jiang <amanda.jiang@oracle.com>, Anthony Scarpino <anthony.scarpino@oracle.com>, Bradford Wetmore <bradford.wetmore@oracle.com>, Jamil Nimeh <jamil.j.nimeh@oracle.com>, John Jiang <sha.jiang@oracle.com>, Rajan Halade <rajan.halade@oracle.com>, Sibabrata Sahoo <sibabrata.sahoo@oracle.com>, Valerie Peng <valerie.peng@oracle.com>, Weijun Wang <weijun.wang@oracle.com>, Xuelei Fan <xuelei.fan@oracle.com>

/*
 * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.

/*
 * @test
 * @bug 4416068 4478803 4479736
 * @summary 4273544 JSSE request for function forceV3ClientHello()
 *          4479736 setEnabledProtocols API does not work correctly
 *          4478803 Need APIs to determine the protocol versions used in an SSL
 *                  session
 *          4701722 protocol mismatch exceptions should be consistent between
 *                  SSLv3 and TLSv1
 * @run main/othervm TestEnabledProtocols
 * @author Ram Marti
 */

import java.io.*;
import java.net.*;
import java.util.*;
import java.security.*;
import javax.net.ssl.*;
import java.security.cert.*;

public class TestEnabledProtocols {

    /*
     * For each of the valid protocols combinations, start a server thread
     * that sets up an SSLServerSocket supporting that protocol. Then run
     * a client thread that attemps to open a connection with all
     * possible protocol combinataion.  Verify that we get handshake
     * exceptions correctly. Whenever the connection is established
     * successfully, verify that the negotiated protocol was correct.
     * See results file in this directory for complete results.
     */

    static final String[][] protocolStrings = {
                                {"TLSv1"},
                                {"TLSv1", "SSLv2Hello"},
                                {"TLSv1", "SSLv3"},
                                {"SSLv3", "SSLv2Hello"},
                                {"SSLv3"},
                                {"TLSv1", "SSLv3", "SSLv2Hello"}
                                };

    static final boolean [][] eXceptionArray = {
        // Do we expect exception?       Protocols supported by the server
        { false, true,  false, true,  true,  true }, // TLSv1
        { false, false, false, true,  true,  false}, // TLSv1,SSLv2Hello
        { false, true,  false, true,  false, true }, // TLSv1,SSLv3
        { true,  true,  false, false, false, false}, // SSLv3, SSLv2Hello
        { true,  true,  false, true,  false, true }, // SSLv3
        { false, false, false, false, false, false } // TLSv1,SSLv3,SSLv2Hello
        };

    static final String[][] protocolSelected = {
        // TLSv1
        { "TLSv1",  null,   "TLSv1",  null,   null,     null },

        // TLSv1,SSLv2Hello
        { "TLSv1", "TLSv1", "TLSv1",  null,   null,    "TLSv1"},

        // TLSv1,SSLv3
        { "TLSv1",  null,   "TLSv1",  null,   "SSLv3",  null },

        // SSLv3, SSLv2Hello
        {  null,    null,   "SSLv3", "SSLv3", "SSLv3",  "SSLv3"},

        // SSLv3
        {  null,    null,   "SSLv3",  null,   "SSLv3",  null },

        // TLSv1,SSLv3,SSLv2Hello
        { "TLSv1", "TLSv1", "TLSv1", "SSLv3", "SSLv3", "TLSv1" }

    };

    /*
     * Where do we find the keystores?
     */
    final static String pathToStores = "../etc";
    static String passwd = "passphrase";
    static String keyStoreFile = "keystore";
    static String trustStoreFile = "truststore";

    /*
     * Is the server ready to serve?
     */
    volatile static boolean serverReady = false;

    /*
     * Turn on SSL debugging?
     */
    final static boolean debug = false;

    // use any free port by default
    volatile int serverPort = 0;

    volatile Exception clientException = null;

    public static void main(String[] args) throws Exception {
        // reset the security property to make sure that the algorithms
        // and keys used in this test are not disabled.
        Security.setProperty("jdk.tls.disabledAlgorithms", "");

        String keyFilename =
            System.getProperty("test.src", "./") + "/" + pathToStores +
                "/" + keyStoreFile;
        String trustFilename =
            System.getProperty("test.src", "./") + "/" + pathToStores +
                "/" + trustStoreFile;

        System.setProperty("javax.net.ssl.keyStore", keyFilename);
        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
        System.setProperty("javax.net.ssl.trustStore", trustFilename);
        System.setProperty("javax.net.ssl.trustStorePassword", passwd);

        if (debug)
            System.setProperty("javax.net.debug", "all");

        new TestEnabledProtocols();
    }

    TestEnabledProtocols() throws Exception  {
        /*
         * Start the tests.
         */
        SSLServerSocketFactory sslssf =
            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
        SSLServerSocket sslServerSocket =
            (SSLServerSocket) sslssf.createServerSocket(serverPort);
        serverPort = sslServerSocket.getLocalPort();
        // sslServerSocket.setNeedClientAuth(true);

        for (int i = 0; i < protocolStrings.length; i++) {
            String [] serverProtocols = protocolStrings[i];
            startServer ss = new startServer(serverProtocols,
                sslServerSocket, protocolStrings.length);
            ss.setDaemon(true);
            ss.start();
            for (int j = 0; j < protocolStrings.length; j++) {
                String [] clientProtocols = protocolStrings[j];
                startClient sc = new startClient(
                    clientProtocols, serverProtocols,
                    eXceptionArray[i][j], protocolSelected[i][j]);
                sc.start();
                sc.join();
                if (clientException != null) {
                    ss.requestStop();
                    throw clientException;
                }
            }
            ss.requestStop();
            System.out.println("Waiting for the server to complete");
            ss.join();
        }
    }

    class startServer extends Thread  {
        private String[] enabledP = null;
        SSLServerSocket sslServerSocket = null;
        int numExpConns;
        volatile boolean stopRequested = false;

        public startServer(String[] enabledProtocols,
                            SSLServerSocket sslServerSocket,
                            int numExpConns) {
            super("Server Thread");
            serverReady = false;
            enabledP = enabledProtocols;
            this.sslServerSocket = sslServerSocket;
            sslServerSocket.setEnabledProtocols(enabledP);
            this.numExpConns = numExpConns;
        }

        public void requestStop() {
            stopRequested = true;
        }

        public void run() {
            int conns = 0;
            while (!stopRequested) {
                SSLSocket socket = null;
                try {
                    serverReady = true;
                    socket = (SSLSocket)sslServerSocket.accept();
                    conns++;

                    // set ready to false. this is just to make the
                    // client wait and synchronise exception messages
                    serverReady = false;
                    socket.startHandshake();
                    SSLSession session = socket.getSession();
                    session.invalidate();

                    InputStream in = socket.getInputStream();
                    OutputStream out = socket.getOutputStream();
                    out.write(280);
                    in.read();

                    socket.close();
                    // sleep for a while so that the server thread can be
                    // stopped
                    Thread.sleep(30);
                } catch (SSLHandshakeException se) {
                    // ignore it; this is part of the testing
                    // log it for debugging
                    System.out.println("Server SSLHandshakeException:");
                    se.printStackTrace(System.out);
                } catch (java.io.InterruptedIOException ioe) {
                    // must have been interrupted, no harm
                    break;
                } catch (java.lang.InterruptedException ie) {
                    // must have been interrupted, no harm
                    break;
                } catch (SSLException ssle) {
                    // The client side may have closed the socket.
                    System.out.println("Server SSLException:");
                    ssle.printStackTrace(System.out);
                } catch (Exception e) {
                    System.out.println("Server exception:");
                    e.printStackTrace(System.out);
                    throw new RuntimeException(e);
                } finally {
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) {
                        // ignore
                    }
                }
                if (conns >= numExpConns) {
                    break;
                }
            }
        }
    }

    private static void showProtocols(String name, String[] protocols) {
        System.out.println("Enabled protocols on the " + name + " are: " + Arrays.asList(protocols));
    }

    class startClient extends Thread {
        boolean hsCompleted = false;
        boolean exceptionExpected = false;
        private String[] enabledP = null;
        private String[] serverP = null; // used to print the result
        private String protocolToUse = null;

        startClient(String[] enabledProtocol,
                    String[] serverP,
                    boolean eXception,
                    String protocol) throws Exception {
            super("Client Thread");
            this.enabledP = enabledProtocol;
            this.serverP = serverP;
            this.exceptionExpected = eXception;
            this.protocolToUse = protocol;
        }

        public void run() {
            SSLSocket sslSocket = null;
            try {
                while (!serverReady) {
                    Thread.sleep(50);
                }
                System.out.flush();
                System.out.println("=== Starting new test run ===");
                showProtocols("server", serverP);
                showProtocols("client", enabledP);

                SSLSocketFactory sslsf =
                    (SSLSocketFactory)SSLSocketFactory.getDefault();
                sslSocket = (SSLSocket)
                    sslsf.createSocket("localhost", serverPort);
                sslSocket.setEnabledProtocols(enabledP);
                sslSocket.startHandshake();

                SSLSession session = sslSocket.getSession();
                session.invalidate();
                String protocolName = session.getProtocol();
                System.out.println("Protocol name after getSession is " +
                    protocolName);

                if (protocolName.equals(protocolToUse)) {
                    System.out.println("** Success **");
                } else {
                    System.out.println("** FAILURE ** ");
                    throw new RuntimeException
                        ("expected protocol " + protocolToUse +
                         " but using " + protocolName);
                }

                InputStream in = sslSocket.getInputStream();
                OutputStream out = sslSocket.getOutputStream();
                in.read();
                out.write(280);

                sslSocket.close();

            } catch (SSLHandshakeException e) {
                if (!exceptionExpected) {
                    System.out.println("Client got UNEXPECTED SSLHandshakeException:");
                    e.printStackTrace(System.out);
                    System.out.println("** FAILURE **");
                    clientException = e;
                } else {
                    System.out.println("Client got expected SSLHandshakeException:");
                    e.printStackTrace(System.out);
                    System.out.println("** Success **");
                }
            } catch (RuntimeException e) {
                clientException = e;
            } catch (Exception e) {
                System.out.println("Client got UNEXPECTED Exception:");
                e.printStackTrace(System.out);
                System.out.println("** FAILURE **");
                clientException = e;
            }
        }
    }

}