Path: blob/master/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java
41152 views
/*1* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 813363226* @summary javax.net.ssl.SSLEngine does not properly handle received27* SSL fatal alerts28* @ignore the dependent implementation details are changed29* @run main/othervm EngineCloseOnAlert30*/3132import java.io.FileInputStream;33import java.io.IOException;34import javax.net.ssl.*;35import java.nio.ByteBuffer;36import java.util.*;37import java.security.*;38import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;3940public class EngineCloseOnAlert {4142private static final String pathToStores = "../etc";43private static final String keyStoreFile = "keystore";44private static final String trustStoreFile = "truststore";45private static final String passwd = "passphrase";46private static final String keyFilename =47System.getProperty("test.src", ".") + "/" + pathToStores +48"/" + keyStoreFile;49private static final String trustFilename =50System.getProperty("test.src", ".") + "/" + pathToStores +51"/" + trustStoreFile;5253private static KeyManagerFactory KMF;54private static TrustManagerFactory TMF;55private static TrustManagerFactory EMPTY_TMF;5657private static final String[] TLS10ONLY = { "TLSv1" };58private static final String[] TLS12ONLY = { "TLSv1.2" };59private static final String[] ONECIPHER =60{ "TLS_RSA_WITH_AES_128_CBC_SHA" };6162public interface TestCase {63public void runTest() throws Exception;64}6566public static void main(String[] args) throws Exception {67int failed = 0;68List<TestCase> testMatrix = new LinkedList<TestCase>() {{69add(clientReceivesAlert);70add(serverReceivesAlert);71}};7273// Create the various key/trust manager factories we'll need74createManagerFactories();7576for (TestCase test : testMatrix) {77try {78test.runTest();79} catch (Exception e) {80System.out.println("Exception in test:\n" + e);81e.printStackTrace(System.out);82failed++;83}84}8586System.out.println("Total tests: " + testMatrix.size() + ", passed: " +87(testMatrix.size() - failed) + ", failed: " + failed);88if (failed > 0) {89throw new RuntimeException("One or more tests failed.");90}91}9293private static final TestCase clientReceivesAlert = new TestCase() {94@Override95public void runTest() throws Exception {96System.out.println("");97System.out.println("=======================================");98System.out.println("Test: Client receives alert from server");99System.out.println("=======================================");100101// For this test, we won't initialize any keystore so the102// server will throw an exception because it has no key/cert to103// match the requested ciphers offered by the client. This104// will generate an alert from the server to the client.105106SSLContext context = SSLContext.getDefault();107SSLEngine client = context.createSSLEngine();108SSLEngine server = context.createSSLEngine();109client.setUseClientMode(true);110server.setUseClientMode(false);111SSLEngineResult clientResult;112SSLEngineResult serverResult;113114ByteBuffer raw = ByteBuffer.allocate(32768);115ByteBuffer plain = ByteBuffer.allocate(32768);116117// Generate the client hello and have the server unwrap it118client.wrap(plain, raw);119checkEngineState(client, NEED_UNWRAP, false, false);120raw.flip();121System.out.println("Client-to-Server:\n-----------------\n" +122dumpHexBytes(raw, 16, "\n", ":"));123124125// The server should need to run a delegated task while processing126// the client hello data.127serverResult = server.unwrap(raw, plain);128checkEngineState(server, NEED_TASK, false, false);129System.out.println("Server result: " + serverResult);130runDelegatedTasks(serverResult, server);131checkEngineState(server, NEED_WRAP, true, false);132133try {134raw.clear();135serverResult = server.wrap(plain, raw);136System.out.println("Server result: " + serverResult);137runDelegatedTasks(serverResult, server);138} catch (SSLException e) {139// This is the expected code path140System.out.println("Server throws exception: " + e);141System.out.println("Server engine state: " +142"isInboundDone = "+ server.isInboundDone() +143", isOutboundDone = " + server.isOutboundDone() +144", handshake status = " + server.getHandshakeStatus());145checkEngineState(server, NEED_WRAP, true, false);146}147raw.clear();148149// The above should show that isInboundDone returns true, and150// handshake status is NEED_WRAP. That is the correct behavior,151// wrap will put a fatal alert message in the buffer.152serverResult = server.wrap(plain, raw);153System.out.println("Server result (wrap after exception): " +154serverResult);155System.out.println("Server engine closure state: isInboundDone="156+ server.isInboundDone() + ", isOutboundDone="157+ server.isOutboundDone());158checkEngineState(server, NEED_UNWRAP, true, true);159raw.flip();160161System.out.println("Server-to-Client:\n-----------------\n" +162dumpHexBytes(raw, 16, "\n", ":"));163164// Client side will read the fatal alert and throw exception.165try {166clientResult = client.unwrap(raw, plain);167System.out.println("Client result (unwrap alert): " +168clientResult);169} catch (SSLException e) {170System.out.println("Client throws exception: " + e);171System.out.println("Engine closure status: isInboundDone="172+ client.isInboundDone() + ", isOutboundDone="173+ client.isOutboundDone() + ", handshake status="174+ client.getHandshakeStatus());175checkEngineState(client, NOT_HANDSHAKING, true, true);176}177raw.clear();178179// Last test, we try to unwrap180clientResult = client.unwrap(raw, plain);181checkEngineState(client, NOT_HANDSHAKING, true, true);182System.out.println("Client result (wrap after exception): " +183clientResult);184}185};186187private static final TestCase serverReceivesAlert = new TestCase() {188@Override189public void runTest() throws Exception {190SSLContext cliContext = SSLContext.getDefault();191SSLContext servContext = SSLContext.getInstance("TLS");192servContext.init(KMF.getKeyManagers(), TMF.getTrustManagers(),193null);194SSLEngine client = cliContext.createSSLEngine();195SSLEngine server = servContext.createSSLEngine();196client.setUseClientMode(true);197client.setEnabledProtocols(TLS12ONLY);198client.setEnabledCipherSuites(ONECIPHER);199server.setUseClientMode(false);200server.setEnabledProtocols(TLS10ONLY);201SSLEngineResult clientResult;202SSLEngineResult serverResult;203ByteBuffer raw = ByteBuffer.allocate(32768);204ByteBuffer plain = ByteBuffer.allocate(32768);205206System.out.println("");207System.out.println("=======================================");208System.out.println("Test: Server receives alert from client");209System.out.println("=======================================");210211// Generate the client hello and have the server unwrap it212checkEngineState(client, NOT_HANDSHAKING, false, false);213client.wrap(plain, raw);214checkEngineState(client, NEED_UNWRAP, false, false);215raw.flip();216System.out.println("Client-to-Server:\n-----------------\n" +217dumpHexBytes(raw, 16, "\n", ":"));218219// The server should need to run a delegated task while processing220// the client hello data.221serverResult = server.unwrap(raw, plain);222checkEngineState(server, NEED_TASK, false, false);223runDelegatedTasks(serverResult, server);224checkEngineState(server, NEED_WRAP, false, false);225raw.compact();226227// The server should now wrap the response back to the client228server.wrap(plain, raw);229checkEngineState(server, NEED_UNWRAP, false, false);230raw.flip();231System.out.println("Server-to-Client:\n-----------------\n" +232dumpHexBytes(raw, 16, "\n", ":"));233234// The client should parse this and throw an exception because235// It is unwiling to do TLS 1.0236clientResult = client.unwrap(raw, plain);237checkEngineState(client, NEED_TASK, false, false);238runDelegatedTasks(clientResult, client);239checkEngineState(client, NEED_UNWRAP, false, false);240241try {242client.unwrap(raw, plain);243} catch (SSLException e) {244System.out.println("Client throws exception: " + e);245System.out.println("Engine closure status: isInboundDone="246+ client.isInboundDone() + ", isOutboundDone="247+ client.isOutboundDone() + ", handshake status="248+ client.getHandshakeStatus());249checkEngineState(client, NEED_WRAP, true, false);250}251raw.clear();252253// Now the client should wrap the exception254client.wrap(plain, raw);255checkEngineState(client, NEED_UNWRAP, true, true);256raw.flip();257System.out.println("Client-to-Server:\n-----------------\n" +258dumpHexBytes(raw, 16, "\n", ":"));259260try {261server.unwrap(raw, plain);262checkEngineState(server, NEED_UNWRAP, false, false);263} catch (SSLException e) {264System.out.println("Server throws exception: " + e);265System.out.println("Engine closure status: isInboundDone="266+ server.isInboundDone() + ", isOutboundDone="267+ server.isOutboundDone() + ", handshake status="268+ server.getHandshakeStatus());269checkEngineState(server, NOT_HANDSHAKING, true, true);270}271raw.clear();272}273};274275276/*277* If the result indicates that we have outstanding tasks to do,278* go ahead and run them in this thread.279*/280private static void runDelegatedTasks(SSLEngineResult result,281SSLEngine engine) throws Exception {282283if (result.getHandshakeStatus() ==284SSLEngineResult.HandshakeStatus.NEED_TASK) {285Runnable runnable;286while ((runnable = engine.getDelegatedTask()) != null) {287System.out.println("\trunning delegated task...");288runnable.run();289}290SSLEngineResult.HandshakeStatus hsStatus =291engine.getHandshakeStatus();292if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {293throw new Exception(294"handshake shouldn't need additional tasks");295}296System.out.println("\tnew HandshakeStatus: " + hsStatus);297}298}299300/**301*302* @param data The array of bytes to dump to stdout.303* @param itemsPerLine The number of bytes to display per line304* if the {@code lineDelim} character is blank then all bytes will be305* printed on a single line.306* @param lineDelim The delimiter between lines307* @param itemDelim The delimiter between bytes308*309* @return The hexdump of the byte array310*/311private static String dumpHexBytes(ByteBuffer data, int itemsPerLine,312String lineDelim, String itemDelim) {313StringBuilder sb = new StringBuilder();314315if (data != null) {316data.mark();317for (int i = 0; i < data.limit(); i++) {318if (i % itemsPerLine == 0 && i != 0) {319sb.append(lineDelim);320}321sb.append(String.format("%02X", data.get(i)));322if (i % itemsPerLine != (itemsPerLine - 1) &&323i != (data.limit() -1)) {324sb.append(itemDelim);325}326}327data.reset();328}329330return sb.toString();331}332333private static void createManagerFactories()334throws GeneralSecurityException, IOException {335KeyStore keystore = KeyStore.getInstance("PKCS12");336KeyStore truststore = KeyStore.getInstance("PKCS12");337KeyStore empty_ts = KeyStore.getInstance("PKCS12");338char[] passphrase = passwd.toCharArray();339340keystore.load(new FileInputStream(keyFilename), passphrase);341truststore.load(new FileInputStream(trustFilename), passphrase);342empty_ts.load(null, "".toCharArray());343344KMF = KeyManagerFactory.getInstance("PKIX");345KMF.init(keystore, passphrase);346TMF = TrustManagerFactory.getInstance("PKIX");347TMF.init(truststore);348EMPTY_TMF = TrustManagerFactory.getInstance("PKIX");349EMPTY_TMF.init(truststore);350}351352private static void checkEngineState(SSLEngine engine,353SSLEngineResult.HandshakeStatus expectedHSStat,354boolean expectedInboundDone, boolean expectedOutboundDone) {355if (engine.getHandshakeStatus() != expectedHSStat ||356engine.isInboundDone() != expectedInboundDone ||357engine.isOutboundDone() != expectedOutboundDone) {358throw 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 {366System.out.println((engine.getUseClientMode() ?367"Client" : "Server") + " handshake status: " +368engine.getHandshakeStatus() + ", inDone = " +369engine.isInboundDone() + ", outDone = " +370engine.isOutboundDone());371}372}373}374375376