jdk/test/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java
changeset 41910 1383904abbd0
equal deleted inserted replaced
41909:7cdc6dfe9c9b 41910:1383904abbd0
       
     1 /*
       
     2  * Copyright (c) 2004, 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 8133632
       
    27  * @summary javax.net.ssl.SSLEngine does not properly handle received
       
    28  * SSL fatal alerts
       
    29  * @run main/othervm EngineCloseOnAlert
       
    30  */
       
    31 
       
    32 import java.io.FileInputStream;
       
    33 import java.io.IOException;
       
    34 import javax.net.ssl.*;
       
    35 import java.nio.ByteBuffer;
       
    36 import java.util.*;
       
    37 import java.security.*;
       
    38 import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
       
    39 
       
    40 public class EngineCloseOnAlert {
       
    41 
       
    42     private static final String pathToStores = "../etc";
       
    43     private static final String keyStoreFile = "keystore";
       
    44     private static final String trustStoreFile = "truststore";
       
    45     private static final String passwd = "passphrase";
       
    46     private static final String keyFilename =
       
    47             System.getProperty("test.src", ".") + "/" + pathToStores +
       
    48                 "/" + keyStoreFile;
       
    49     private static final String trustFilename =
       
    50             System.getProperty("test.src", ".") + "/" + pathToStores +
       
    51                 "/" + trustStoreFile;
       
    52 
       
    53     private static KeyManagerFactory KMF;
       
    54     private static TrustManagerFactory TMF;
       
    55     private static TrustManagerFactory EMPTY_TMF;
       
    56 
       
    57     private static final String[] TLS10ONLY = { "TLSv1" };
       
    58     private static final String[] TLS12ONLY = { "TLSv1.2" };
       
    59     private static final String[] ONECIPHER =
       
    60             { "TLS_RSA_WITH_AES_128_CBC_SHA" };
       
    61 
       
    62     public interface TestCase {
       
    63         public void runTest() throws Exception;
       
    64     }
       
    65 
       
    66     public static void main(String[] args) throws Exception {
       
    67         int failed = 0;
       
    68         List<TestCase> testMatrix = new LinkedList<TestCase>() {{
       
    69             add(clientReceivesAlert);
       
    70             add(serverReceivesAlert);
       
    71         }};
       
    72 
       
    73         // Create the various key/trust manager factories we'll need
       
    74         createManagerFactories();
       
    75 
       
    76         for (TestCase test : testMatrix) {
       
    77             try {
       
    78                 test.runTest();
       
    79             } catch (Exception e) {
       
    80                 System.out.println("Exception in test:\n" + e);
       
    81                 e.printStackTrace(System.out);
       
    82                 failed++;
       
    83             }
       
    84         }
       
    85 
       
    86         System.out.println("Total tests: " + testMatrix.size() + ", passed: " +
       
    87                 (testMatrix.size() - failed) + ", failed: " + failed);
       
    88         if (failed > 0) {
       
    89             throw new RuntimeException("One or more tests failed.");
       
    90         }
       
    91     }
       
    92 
       
    93     private static final TestCase clientReceivesAlert = new TestCase() {
       
    94         @Override
       
    95         public void runTest() throws Exception {
       
    96             System.out.println("");
       
    97             System.out.println("=======================================");
       
    98             System.out.println("Test: Client receives alert from server");
       
    99             System.out.println("=======================================");
       
   100 
       
   101             // For this test, we won't initialize any keystore so the
       
   102             // server will throw an exception because it has no key/cert to
       
   103             // match the requested ciphers offered by the client.  This
       
   104             // will generate an alert from the server to the client.
       
   105 
       
   106             SSLContext context = SSLContext.getDefault();
       
   107             SSLEngine client = context.createSSLEngine();
       
   108             SSLEngine server = context.createSSLEngine();
       
   109             client.setUseClientMode(true);
       
   110             server.setUseClientMode(false);
       
   111             SSLEngineResult clientResult;
       
   112             SSLEngineResult serverResult;
       
   113 
       
   114             ByteBuffer raw = ByteBuffer.allocate(32768);
       
   115             ByteBuffer plain = ByteBuffer.allocate(32768);
       
   116 
       
   117             // Generate the client hello and have the server unwrap it
       
   118             client.wrap(plain, raw);
       
   119             checkEngineState(client, NEED_UNWRAP, false, false);
       
   120             raw.flip();
       
   121             System.out.println("Client-to-Server:\n-----------------\n" +
       
   122                     dumpHexBytes(raw, 16, "\n", ":"));
       
   123 
       
   124 
       
   125             // The server should need to run a delegated task while processing
       
   126             // the client hello data.
       
   127             serverResult = server.unwrap(raw, plain);
       
   128             checkEngineState(server, NEED_TASK, false, false);
       
   129             System.out.println("Server result: " + serverResult);
       
   130             runDelegatedTasks(serverResult, server);
       
   131             checkEngineState(server, NEED_WRAP, true, false);
       
   132 
       
   133             try {
       
   134                 raw.clear();
       
   135                 serverResult = server.wrap(plain, raw);
       
   136                 System.out.println("Server result: " + serverResult);
       
   137                 runDelegatedTasks(serverResult, server);
       
   138             } catch (SSLException e) {
       
   139                 // This is the expected code path
       
   140                 System.out.println("Server throws exception: " + e);
       
   141                 System.out.println("Server engine state: " +
       
   142                         "isInboundDone = "+ server.isInboundDone() +
       
   143                         ", isOutboundDone = " + server.isOutboundDone() +
       
   144                         ", handshake status = " + server.getHandshakeStatus());
       
   145                 checkEngineState(server, NEED_WRAP, true, false);
       
   146             }
       
   147             raw.clear();
       
   148 
       
   149             // The above should show that isInboundDone returns true, and
       
   150             // handshake status is NEED_WRAP. That is the correct behavior,
       
   151             // wrap will put a fatal alert message in the buffer.
       
   152             serverResult = server.wrap(plain, raw);
       
   153             System.out.println("Server result (wrap after exception): " +
       
   154                     serverResult);
       
   155             System.out.println("Server engine closure state: isInboundDone="
       
   156                     + server.isInboundDone() + ", isOutboundDone="
       
   157                     + server.isOutboundDone());
       
   158             checkEngineState(server, NEED_UNWRAP, true, true);
       
   159             raw.flip();
       
   160 
       
   161             System.out.println("Server-to-Client:\n-----------------\n" +
       
   162                     dumpHexBytes(raw, 16, "\n", ":"));
       
   163 
       
   164             // Client side will read the fatal alert and throw exception.
       
   165             try {
       
   166                 clientResult = client.unwrap(raw, plain);
       
   167                 System.out.println("Client result (unwrap alert): " +
       
   168                     clientResult);
       
   169             } catch (SSLException e) {
       
   170                 System.out.println("Client throws exception: " + e);
       
   171                 System.out.println("Engine closure status: isInboundDone="
       
   172                         + client.isInboundDone() + ", isOutboundDone="
       
   173                         + client.isOutboundDone() + ", handshake status="
       
   174                         + client.getHandshakeStatus());
       
   175                 checkEngineState(client, NOT_HANDSHAKING, true, true);
       
   176             }
       
   177             raw.clear();
       
   178 
       
   179             // Last test, we try to unwrap
       
   180             clientResult = client.unwrap(raw, plain);
       
   181             checkEngineState(client, NOT_HANDSHAKING, true, true);
       
   182             System.out.println("Client result (wrap after exception): " +
       
   183                     clientResult);
       
   184         }
       
   185     };
       
   186 
       
   187     private static final TestCase serverReceivesAlert = new TestCase() {
       
   188         @Override
       
   189         public void runTest() throws Exception {
       
   190             SSLContext cliContext = SSLContext.getDefault();
       
   191             SSLContext servContext = SSLContext.getInstance("TLS");
       
   192             servContext.init(KMF.getKeyManagers(), TMF.getTrustManagers(),
       
   193                     null);
       
   194             SSLEngine client = cliContext.createSSLEngine();
       
   195             SSLEngine server = servContext.createSSLEngine();
       
   196             client.setUseClientMode(true);
       
   197             client.setEnabledProtocols(TLS12ONLY);
       
   198             client.setEnabledCipherSuites(ONECIPHER);
       
   199             server.setUseClientMode(false);
       
   200             server.setEnabledProtocols(TLS10ONLY);
       
   201             SSLEngineResult clientResult;
       
   202             SSLEngineResult serverResult;
       
   203             ByteBuffer raw = ByteBuffer.allocate(32768);
       
   204             ByteBuffer plain = ByteBuffer.allocate(32768);
       
   205 
       
   206             System.out.println("");
       
   207             System.out.println("=======================================");
       
   208             System.out.println("Test: Server receives alert from client");
       
   209             System.out.println("=======================================");
       
   210 
       
   211             // Generate the client hello and have the server unwrap it
       
   212             checkEngineState(client, NOT_HANDSHAKING, false, false);
       
   213             client.wrap(plain, raw);
       
   214             checkEngineState(client, NEED_UNWRAP, false, false);
       
   215             raw.flip();
       
   216             System.out.println("Client-to-Server:\n-----------------\n" +
       
   217                     dumpHexBytes(raw, 16, "\n", ":"));
       
   218 
       
   219             // The server should need to run a delegated task while processing
       
   220             // the client hello data.
       
   221             serverResult = server.unwrap(raw, plain);
       
   222             checkEngineState(server, NEED_TASK, false, false);
       
   223             runDelegatedTasks(serverResult, server);
       
   224             checkEngineState(server, NEED_WRAP, false, false);
       
   225             raw.compact();
       
   226 
       
   227             // The server should now wrap the response back to the client
       
   228             server.wrap(plain, raw);
       
   229             checkEngineState(server, NEED_UNWRAP, false, false);
       
   230             raw.flip();
       
   231             System.out.println("Server-to-Client:\n-----------------\n" +
       
   232                     dumpHexBytes(raw, 16, "\n", ":"));
       
   233 
       
   234             // The client should parse this and throw an exception because
       
   235             // It is unwiling to do TLS 1.0
       
   236             clientResult = client.unwrap(raw, plain);
       
   237             checkEngineState(client, NEED_TASK, false, false);
       
   238             runDelegatedTasks(clientResult, client);
       
   239             checkEngineState(client, NEED_UNWRAP, false, false);
       
   240 
       
   241             try {
       
   242                 client.unwrap(raw, plain);
       
   243             } catch (SSLException e) {
       
   244                 System.out.println("Client throws exception: " + e);
       
   245                 System.out.println("Engine closure status: isInboundDone="
       
   246                         + client.isInboundDone() + ", isOutboundDone="
       
   247                         + client.isOutboundDone() + ", handshake status="
       
   248                         + client.getHandshakeStatus());
       
   249                 checkEngineState(client, NEED_WRAP, true, false);
       
   250             }
       
   251             raw.clear();
       
   252 
       
   253             // Now the client should wrap the exception
       
   254             client.wrap(plain, raw);
       
   255             checkEngineState(client, NEED_UNWRAP, true, true);
       
   256             raw.flip();
       
   257             System.out.println("Client-to-Server:\n-----------------\n" +
       
   258                     dumpHexBytes(raw, 16, "\n", ":"));
       
   259 
       
   260             try {
       
   261                 server.unwrap(raw, plain);
       
   262                 checkEngineState(server, NEED_UNWRAP, false, false);
       
   263             } catch (SSLException e) {
       
   264                 System.out.println("Server throws exception: " + e);
       
   265                 System.out.println("Engine closure status: isInboundDone="
       
   266                         + server.isInboundDone() + ", isOutboundDone="
       
   267                         + server.isOutboundDone() + ", handshake status="
       
   268                         + server.getHandshakeStatus());
       
   269                 checkEngineState(server, NOT_HANDSHAKING, true, true);
       
   270             }
       
   271             raw.clear();
       
   272         }
       
   273     };
       
   274 
       
   275 
       
   276     /*
       
   277      * If the result indicates that we have outstanding tasks to do,
       
   278      * go ahead and run them in this thread.
       
   279      */
       
   280     private static void runDelegatedTasks(SSLEngineResult result,
       
   281             SSLEngine engine) throws Exception {
       
   282 
       
   283         if (result.getHandshakeStatus() ==
       
   284                 SSLEngineResult.HandshakeStatus.NEED_TASK) {
       
   285             Runnable runnable;
       
   286             while ((runnable = engine.getDelegatedTask()) != null) {
       
   287                 System.out.println("\trunning delegated task...");
       
   288                 runnable.run();
       
   289             }
       
   290             SSLEngineResult.HandshakeStatus hsStatus =
       
   291                     engine.getHandshakeStatus();
       
   292             if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
       
   293                 throw new Exception(
       
   294                     "handshake shouldn't need additional tasks");
       
   295             }
       
   296             System.out.println("\tnew HandshakeStatus: " + hsStatus);
       
   297         }
       
   298     }
       
   299 
       
   300     /**
       
   301      *
       
   302      * @param data The array of bytes to dump to stdout.
       
   303      * @param itemsPerLine The number of bytes to display per line
       
   304      * if the {@code lineDelim} character is blank then all bytes will be
       
   305      * printed on a single line.
       
   306      * @param lineDelim The delimiter between lines
       
   307      * @param itemDelim The delimiter between bytes
       
   308      *
       
   309      * @return The hexdump of the byte array
       
   310      */
       
   311     private static String dumpHexBytes(ByteBuffer data, int itemsPerLine,
       
   312             String lineDelim, String itemDelim) {
       
   313         StringBuilder sb = new StringBuilder();
       
   314 
       
   315         if (data != null) {
       
   316             data.mark();
       
   317             for (int i = 0; i < data.limit(); i++) {
       
   318                 if (i % itemsPerLine == 0 && i != 0) {
       
   319                     sb.append(lineDelim);
       
   320                 }
       
   321                 sb.append(String.format("%02X", data.get(i)));
       
   322                 if (i % itemsPerLine != (itemsPerLine - 1) &&
       
   323                         i != (data.limit() -1)) {
       
   324                     sb.append(itemDelim);
       
   325                 }
       
   326             }
       
   327             data.reset();
       
   328         }
       
   329 
       
   330         return sb.toString();
       
   331     }
       
   332 
       
   333     private static void createManagerFactories()
       
   334             throws GeneralSecurityException, IOException {
       
   335         KeyStore keystore = KeyStore.getInstance("PKCS12");
       
   336         KeyStore truststore = KeyStore.getInstance("PKCS12");
       
   337         KeyStore empty_ts = KeyStore.getInstance("PKCS12");
       
   338         char[] passphrase = passwd.toCharArray();
       
   339 
       
   340         keystore.load(new FileInputStream(keyFilename), passphrase);
       
   341         truststore.load(new FileInputStream(trustFilename), passphrase);
       
   342         empty_ts.load(null, "".toCharArray());
       
   343 
       
   344         KMF = KeyManagerFactory.getInstance("PKIX");
       
   345         KMF.init(keystore, passphrase);
       
   346         TMF = TrustManagerFactory.getInstance("PKIX");
       
   347         TMF.init(truststore);
       
   348         EMPTY_TMF = TrustManagerFactory.getInstance("PKIX");
       
   349         EMPTY_TMF.init(truststore);
       
   350     }
       
   351 
       
   352     private static void checkEngineState(SSLEngine engine,
       
   353             SSLEngineResult.HandshakeStatus expectedHSStat,
       
   354             boolean expectedInboundDone, boolean expectedOutboundDone) {
       
   355         if (engine.getHandshakeStatus() != expectedHSStat ||
       
   356                 engine.isInboundDone() != expectedInboundDone ||
       
   357                 engine.isOutboundDone() != expectedOutboundDone) {
       
   358             throw new RuntimeException("Error: engine not in expected state\n" +
       
   359                     "Expected: state = " + expectedHSStat +
       
   360                     ", inDone = " + expectedInboundDone +
       
   361                     ", outDone = " + expectedOutboundDone + "\n" +
       
   362                     "Actual: state = " + engine.getHandshakeStatus() +
       
   363                     ", inDone = " + engine.isInboundDone() +
       
   364                     ", outDone = " + engine.isOutboundDone());
       
   365         } else {
       
   366             System.out.println((engine.getUseClientMode() ?
       
   367                     "Client" : "Server") + " handshake status: " +
       
   368                     engine.getHandshakeStatus() + ", inDone = " +
       
   369                     engine.isInboundDone() + ", outDone = " +
       
   370                     engine.isOutboundDone());
       
   371         }
       
   372     }
       
   373 }