/* * Copyright (c) 2018, 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. */ /* * @test * @bug 8210918 8210334 8209916 * @summary Add test to exercise server-side client hello processing * @run main/othervm ClientHelloProcessing noPskNoKexModes * @run main/othervm ClientHelloProcessing noPskYesKexModes * @run main/othervm ClientHelloProcessing yesPskNoKexModes * @run main/othervm ClientHelloProcessing yesPskYesKexModes * @run main/othervm ClientHelloProcessing supGroupsSect163k1 */ /* * SunJSSE does not support dynamic system properties, no way to re-use * system properties in samevm/agentvm mode. */ import java.io.FileInputStream; import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import java.io.IOException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Map; import java.util.HashMap; import java.util.Objects; /* * If you wish to add test cases, the following must be done: * 1. Add a @run line with the parameter being a name for the test case * 2. Create the ClientHello as a byte[]. It should be a complete TLS * record, but does not need upper layer headers like TCP, IP, Ethernet, etc. * 3. Create a new TestCase instance, see "noPskNoKexModes" as an example * 4. Create a mapping between the test case name in your @run line and the * TestCase object you created in step #3. Add this to TESTMAP. */ public class ClientHelloProcessing { private static final ByteBuffer SERVOUTBUF = ByteBuffer.wrap("Server Side".getBytes()); private static final String pathToStores = "../etc"; private static final String keyStoreFile = "keystore"; private static final String trustStoreFile = "truststore"; private static final String passwd = "passphrase"; private static final String keyFilename = System.getProperty("test.src", ".") + "/" + pathToStores + "/" + keyStoreFile; private static final String trustFilename = System.getProperty("test.src", ".") + "/" + pathToStores + "/" + trustStoreFile; private static TrustManagerFactory trustMgrFac = null; private static KeyManagerFactory keyMgrFac = null; // Canned client hello messages // These were created from packet captures using openssl 1.1.1's // s_client utility. The captured TLS record containing the client // hello was then manually edited to remove or add fields if the s_client // utility could not be used to generate a message with the desired // extensions. When manually altering the hello messages, care must // be taken to change the lengths of the extensions themselves, the // extensions vector length, the handshake message length, and the TLS // record length. // Client Hello with the pre_shared_key and psk_key_exchange_modes // both absent. Required manual removal of the psk_key_exchange_modes // extension. Similarly formed Client Hello messages may be generated // by clients that don't support pre-shared keys. // // TLSv1.3 Record Layer: Handshake Protocol: Client Hello // Content Type: Handshake (22) // Version: TLS 1.0 (0x0301) // Length: 256 // Handshake Protocol: Client Hello // Handshake Type: Client Hello (1) // Length: 252 // Version: TLS 1.2 (0x0303) // Random: 9b796ad0cbd559fb48fc4ba32da5bb8c1ef9a7da85231860... // Session ID Length: 32 // Session ID: fe8411205bc99a506952f5c28569facb96ff0f37621be072... // Cipher Suites Length: 8 // Cipher Suites (4 suites) // Compression Methods Length: 1 // Compression Methods (1 method) // Extensions Length: 171 // Extension: server_name (len=14) // Extension: ec_point_formats (len=4) // Extension: supported_groups (len=4) // Extension: SessionTicket TLS (len=0) // Extension: status_request (len=5) // Extension: encrypt_then_mac (len=0) // Extension: extended_master_secret (len=0) // Extension: signature_algorithms (len=30) // Extension: supported_versions (len=3) // Extension: key_share (len=71) private static final byte[] CLIHELLO_NOPSK_NOPSKEXMODE = { 22, 3, 1, 1, 0, 1, 0, 0, -4, 3, 3, -101, 121, 106, -48, -53, -43, 89, -5, 72, -4, 75, -93, 45, -91, -69, -116, 30, -7, -89, -38, -123, 35, 24, 96, 29, -93, -22, 10, -97, -15, -11, 3, 32, -2, -124, 17, 32, 91, -55, -102, 80, 105, 82, -11, -62, -123, 105, -6, -53, -106, -1, 15, 55, 98, 27, -32, 114, -126, -13, 42, -104, -102, 37, -65, 52, 0, 8, 19, 2, 19, 3, 19, 1, 0, -1, 1, 0, 0, -85, 0, 0, 0, 14, 0, 12, 0, 0, 9, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 11, 0, 4, 3, 0, 1, 2, 0, 10, 0, 4, 0, 2, 0, 23, 0, 35, 0, 0, 0, 5, 0, 5, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 13, 0, 30, 0, 28, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8, 8, 9, 8, 10, 8, 11, 8, 4, 8, 5, 8, 6, 4, 1, 5, 1, 6, 1, 0, 43, 0, 3, 2, 3, 4, 0, 51, 0, 71, 0, 69, 0, 23, 0, 65, 4, 125, -92, -50, -91, -39, -55, -114, 0, 22, 2, -50, 123, -126, 0, -94, 100, -119, -106, 125, -81, -24, 51, -84, 25, 25, -115, 13, -17, -20, 93, 68, -97, -79, -98, 91, 86, 91, -114, 123, 119, -87, -12, 32, 63, -41, 50, 126, -70, 96, 33, -6, 94, -7, -68, 54, -47, 53, 0, 88, 40, -48, -102, -50, 88 }; // Client Hello with the pre_shared_key extension absent but // containing the psk_key_exchange_modes extension asserted. No // manual modification was necessary. // // TLSv1.3 Record Layer: Handshake Protocol: Client Hello // Content Type: Handshake (22) // Version: TLS 1.0 (0x0301) // Length: 262 // Handshake Protocol: Client Hello // Handshake Type: Client Hello (1) // Length: 258 // Version: TLS 1.2 (0x0303) // Random: 9b796ad0cbd559fb48fc4ba32da5bb8c1ef9a7da85231860... // Session ID Length: 32 // Session ID: fe8411205bc99a506952f5c28569facb96ff0f37621be072... // Cipher Suites Length: 8 // Cipher Suites (4 suites) // Compression Methods Length: 1 // Compression Methods (1 method) // Extensions Length: 177 // Extension: server_name (len=14) // Extension: ec_point_formats (len=4) // Extension: supported_groups (len=4) // Extension: SessionTicket TLS (len=0) // Extension: status_request (len=5) // Extension: encrypt_then_mac (len=0) // Extension: extended_master_secret (len=0) // Extension: signature_algorithms (len=30) // Extension: supported_versions (len=3) // Extension: psk_key_exchange_modes (len=2) // Type: psk_key_exchange_modes (45) // Length: 2 // PSK Key Exchange Modes Length: 1 // PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1) // Extension: key_share (len=71) private static final byte[] CLIHELLO_NOPSK_YESPSKEXMODE = { 22, 3, 1, 1, 6, 1, 0, 1, 2, 3, 3, -101, 121, 106, -48, -53, -43, 89, -5, 72, -4, 75, -93, 45, -91, -69, -116, 30, -7, -89, -38, -123, 35, 24, 96, 29, -93, -22, 10, -97, -15, -11, 3, 32, -2, -124, 17, 32, 91, -55, -102, 80, 105, 82, -11, -62, -123, 105, -6, -53, -106, -1, 15, 55, 98, 27, -32, 114, -126, -13, 42, -104, -102, 37, -65, 52, 0, 8, 19, 2, 19, 3, 19, 1, 0, -1, 1, 0, 0, -79, 0, 0, 0, 14, 0, 12, 0, 0, 9, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 11, 0, 4, 3, 0, 1, 2, 0, 10, 0, 4, 0, 2, 0, 23, 0, 35, 0, 0, 0, 5, 0, 5, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 13, 0, 30, 0, 28, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8, 8, 9, 8, 10, 8, 11, 8, 4, 8, 5, 8, 6, 4, 1, 5, 1, 6, 1, 0, 43, 0, 3, 2, 3, 4, 0, 45, 0, 2, 1, 1, 0, 51, 0, 71, 0, 69, 0, 23, 0, 65, 4, 125, -92, -50, -91, -39, -55, -114, 0, 22, 2, -50, 123, -126, 0, -94, 100, -119, -106, 125, -81, -24, 51, -84, 25, 25, -115, 13, -17, -20, 93, 68, -97, -79, -98, 91, 86, 91, -114, 123, 119, -87, -12, 32, 63, -41, 50, 126, -70, 96, 33, -6, 94, -7, -68, 54, -47, 53, 0, 88, 40, -48, -102, -50, 88 }; // Client Hello with pre_shared_key asserted and psk_key_exchange_modes // absent. This is a violation of RFC 8446. This required manual // removal of the psk_key_exchange_modes extension. // // TLSv1.3 Record Layer: Handshake Protocol: Client Hello // Content Type: Handshake (22) // Version: TLS 1.0 (0x0301) // Length: 318 // Handshake Protocol: Client Hello // Handshake Type: Client Hello (1) // Length: 314 // Version: TLS 1.2 (0x0303) // Random: e730e42336a19ed9fdb42919c65769132e9e779a797f188c... // Session ID Length: 32 // Session ID: 6c6ed31408042fabd0c47fdeee6d19de2d6795e37590f00e... // Cipher Suites Length: 8 // Cipher Suites (4 suites) // Compression Methods Length: 1 // Compression Methods (1 method) // Extensions Length: 233 // Extension: server_name (len=14) // Extension: ec_point_formats (len=4) // Extension: supported_groups (len=4) // Extension: SessionTicket TLS (len=0) // Extension: status_request (len=5) // Extension: encrypt_then_mac (len=0) // Extension: extended_master_secret (len=0) // Extension: signature_algorithms (len=30) // Extension: supported_versions (len=3) // Extension: key_share (len=71) // Extension: pre_shared_key (len=58) // Type: pre_shared_key (41) // Length: 58 // Pre-Shared Key extension // Identities Length: 21 // PSK Identity (length: 15) // Identity Length: 15 // Identity: 436c69656e745f6964656e74697479 // Obfuscated Ticket Age: 0 // PSK Binders length: 33 // PSK Binders private static final byte[] CLIHELLO_YESPSK_NOPSKEXMODE = { 22, 3, 1, 1, 62, 1, 0, 1, 58, 3, 3, -25, 48, -28, 35, 54, -95, -98, -39, -3, -76, 41, 25, -58, 87, 105, 19, 46, -98, 119, -102, 121, 127, 24, -116, -9, -99, 22, 116, -97, 90, 73, -18, 32, 108, 110, -45, 20, 8, 4, 47, -85, -48, -60, 127, -34, -18, 109, 25, -34, 45, 103, -107, -29, 117, -112, -16, 14, -5, -24, 24, 61, -9, 28, -119, -73, 0, 8, 19, 2, 19, 3, 19, 1, 0, -1, 1, 0, 0, -23, 0, 0, 0, 14, 0, 12, 0, 0, 9, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 11, 0, 4, 3, 0, 1, 2, 0, 10, 0, 4, 0, 2, 0, 23, 0, 35, 0, 0, 0, 5, 0, 5, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 13, 0, 30, 0, 28, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8, 8, 9, 8, 10, 8, 11, 8, 4, 8, 5, 8, 6, 4, 1, 5, 1, 6, 1, 0, 43, 0, 3, 2, 3, 4, 0, 51, 0, 71, 0, 69, 0, 23, 0, 65, 4, -6, 101, 105, -2, -6, 85, -99, -37, 112, 90, 44, -123, -107, 4, -12, -64, 92, 40, 100, 22, -53, -124, 54, 56, 102, 25, 76, -86, -1, 6, 110, 95, 92, -86, -35, -101, 115, 85, 99, 19, 6, -43, 105, -37, -92, 53, -97, 84, -1, -53, 87, -53, -107, -13, -14, 32, 101, -35, 39, 102, -17, -119, -25, -51, 0, 41, 0, 58, 0, 21, 0, 15, 67, 108, 105, 101, 110, 116, 95, 105, 100, 101, 110, 116, 105, 116, 121, 0, 0, 0, 0, 0, 33, 32, -113, -27, -44, -71, -68, -26, -47, 57, -82, -29, -13, -61, 77, 52, -60, 27, 74, -120, -104, 102, 21, 121, 0, 48, 43, -40, -19, -67, 57, -20, 97, 23 }; // Client Hello containing both pre_shared_key and psk_key_exchange_modes // extensions. Generation of this hello was done by adding // "-psk a1b2c3d4" to the s_client command. // // TLSv1.3 Record Layer: Handshake Protocol: Client Hello // Content Type: Handshake (22) // Version: TLS 1.0 (0x0301) // Length: 324 // Handshake Protocol: Client Hello // Handshake Type: Client Hello (1) // Length: 320 // Version: TLS 1.2 (0x0303) // Random: e730e42336a19ed9fdb42919c65769132e9e779a797f188c... // Session ID Length: 32 // Session ID: 6c6ed31408042fabd0c47fdeee6d19de2d6795e37590f00e... // Cipher Suites Length: 8 // Cipher Suites (4 suites) // Compression Methods Length: 1 // Compression Methods (1 method) // Extensions Length: 239 // Extension: server_name (len=14) // Extension: ec_point_formats (len=4) // Extension: supported_groups (len=4) // Extension: SessionTicket TLS (len=0) // Extension: status_request (len=5) // Extension: encrypt_then_mac (len=0) // Extension: extended_master_secret (len=0) // Extension: signature_algorithms (len=30) // Extension: supported_versions (len=3) // Extension: psk_key_exchange_modes (len=2) // Type: psk_key_exchange_modes (45) // Length: 2 // PSK Key Exchange Modes Length: 1 // PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1) // Extension: key_share (len=71) // Extension: pre_shared_key (len=58) // Type: pre_shared_key (41) // Length: 58 // Pre-Shared Key extension // Identities Length: 21 // PSK Identity (length: 15) // Identity Length: 15 // Identity: 436c69656e745f6964656e74697479 // Obfuscated Ticket Age: 0 // PSK Binders length: 33 // PSK Binders private static final byte[] CLIHELLO_YESPSK_YESPSKEXMODE = { 22, 3, 1, 1, 68, 1, 0, 1, 64, 3, 3, -25, 48, -28, 35, 54, -95, -98, -39, -3, -76, 41, 25, -58, 87, 105, 19, 46, -98, 119, -102, 121, 127, 24, -116, -9, -99, 22, 116, -97, 90, 73, -18, 32, 108, 110, -45, 20, 8, 4, 47, -85, -48, -60, 127, -34, -18, 109, 25, -34, 45, 103, -107, -29, 117, -112, -16, 14, -5, -24, 24, 61, -9, 28, -119, -73, 0, 8, 19, 2, 19, 3, 19, 1, 0, -1, 1, 0, 0, -17, 0, 0, 0, 14, 0, 12, 0, 0, 9, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 11, 0, 4, 3, 0, 1, 2, 0, 10, 0, 4, 0, 2, 0, 23, 0, 35, 0, 0, 0, 5, 0, 5, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 13, 0, 30, 0, 28, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8, 8, 9, 8, 10, 8, 11, 8, 4, 8, 5, 8, 6, 4, 1, 5, 1, 6, 1, 0, 43, 0, 3, 2, 3, 4, 0, 45, 0, 2, 1, 1, 0, 51, 0, 71, 0, 69, 0, 23, 0, 65, 4, -6, 101, 105, -2, -6, 85, -99, -37, 112, 90, 44, -123, -107, 4, -12, -64, 92, 40, 100, 22, -53, -124, 54, 56, 102, 25, 76, -86, -1, 6, 110, 95, 92, -86, -35, -101, 115, 85, 99, 19, 6, -43, 105, -37, -92, 53, -97, 84, -1, -53, 87, -53, -107, -13, -14, 32, 101, -35, 39, 102, -17, -119, -25, -51, 0, 41, 0, 58, 0, 21, 0, 15, 67, 108, 105, 101, 110, 116, 95, 105, 100, 101, 110, 116, 105, 116, 121, 0, 0, 0, 0, 0, 33, 32, -113, -27, -44, -71, -68, -26, -47, 57, -82, -29, -13, -61, 77, 52, -60, 27, 74, -120, -104, 102, 21, 121, 0, 48, 43, -40, -19, -67, 57, -20, 97, 23 }; // Client Hello with sect163k1 and secp256r1 as supported groups. This // test covers an error condition where a known, supported curve that is // not in the default enabled set of curves would cause failures. // Generation of this hello was done using "-curves sect163k1:prime256v1" // as an option to s_client. // // TLSv1.2 Record Layer: Handshake Protocol: Client Hello // Content Type: Handshake (22) // Version: TLS 1.0 (0x0301) // Length: 210 // Handshake Protocol: Client Hello // Handshake Type: Client Hello (1) // Length: 206 // Version: TLS 1.2 (0x0303) // Random: 05cbae9b834851d856355b72601cb67b7cd4eb51f29ed50b... // Session ID Length: 0 // Cipher Suites Length: 56 // Cipher Suites (28 suites) // Compression Methods Length: 1 // Compression Methods (1 method) // Extensions Length: 109 // Extension: server_name (len=14) // Extension: ec_point_formats (len=4) // Extension: supported_groups (len=6) // Type: supported_groups (10) // Length: 6 // Supported Groups List Length: 4 // Supported Groups (2 groups) // Supported Group: sect163k1 (0x0001) // Supported Group: secp256r1 (0x0017) // Extension: SessionTicket TLS (len=0) // Extension: status_request (len=5) // Extension: encrypt_then_mac (len=0) // Extension: extended_master_secret (len=0) // Extension: signature_algorithms (len=48) private static final byte[] CLIHELLO_SUPGRP_SECT163K1 = { 22, 3, 1, 0, -46, 1, 0, 0, -50, 3, 3, 5, -53, -82, -101, -125, 72, 81, -40, 86, 53, 91, 114, 96, 28, -74, 123, 124, -44, -21, 81, -14, -98, -43, 11, 90, -87, -106, 13, 63, -62, 100, 111, 0, 0, 56, -64, 44, -64, 48, 0, -97, -52, -87, -52, -88, -52, -86, -64, 43, -64, 47, 0, -98, -64, 36, -64, 40, 0, 107, -64, 35, -64, 39, 0, 103, -64, 10, -64, 20, 0, 57, -64, 9, -64, 19, 0, 51, 0, -99, 0, -100, 0, 61, 0, 60, 0, 53, 0, 47, 0, -1, 1, 0, 0, 109, 0, 0, 0, 14, 0, 12, 0, 0, 9, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 11, 0, 4, 3, 0, 1, 2, 0, 10, 0, 6, 0, 4, 0, 1, 0, 23, 0, 35, 0, 0, 0, 5, 0, 5, 1, 0, 0, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 13, 0, 48, 0, 46, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8, 8, 9, 8, 10, 8, 11, 8, 4, 8, 5, 8, 6, 4, 1, 5, 1, 6, 1, 3, 3, 2, 3, 3, 1, 2, 1, 3, 2, 2, 2, 4, 2, 5, 2, 6, 2 }; public static interface TestCase { void execTest() throws Exception; } private static final Map TESTMAP = new HashMap<>(); public static void main(String[] args) throws Exception { boolean allGood = true; System.setProperty("javax.net.debug", "ssl:handshake"); trustMgrFac = makeTrustManagerFactory(trustFilename, passwd); keyMgrFac = makeKeyManagerFactory(keyFilename, passwd); // Populate the test map TESTMAP.put("noPskNoKexModes", noPskNoKexModes); TESTMAP.put("noPskYesKexModes", noPskYesKexModes); TESTMAP.put("yesPskNoKexModes", yesPskNoKexModes); TESTMAP.put("yesPskYesKexModes", yesPskYesKexModes); TESTMAP.put("supGroupsSect163k1", supGroupsSect163k1); if (args == null || args.length < 1) { throw new Exception("FAIL: Test @run line is missing a test label"); } // Pull the test to run from the test map. TestCase test = Objects.requireNonNull(TESTMAP.get(args[0]), "No TestCase found for test label " + args[0]); test.execTest(); } /** * Test case to cover hellos with no pre_shared_key nor * psk_key_exchange_modes extensions. Clients not supporting PSK at all * may send hellos like this. */ private static final TestCase noPskNoKexModes = new TestCase() { @Override public void execTest() throws Exception { System.out.println("\nTest: PSK = No, PSKEX = No"); processClientHello("TLS", CLIHELLO_NOPSK_NOPSKEXMODE); System.out.println("PASS"); } }; /** * Test case to cover hellos with no pre_shared_key but have the * psk_key_exchange_modes extension. This kind of hello is seen from * some popular browsers and test clients. */ private static final TestCase noPskYesKexModes = new TestCase() { @Override public void execTest() throws Exception { System.out.println("\nTest: PSK = No, PSKEX = Yes"); processClientHello("TLS", CLIHELLO_NOPSK_YESPSKEXMODE); System.out.println("PASS"); } }; /** * Test case using a client hello with the pre_shared_key extension but * no psk_key_exchange_modes extension present. This is a violation of * 8446 and should cause an exception when unwrapped and processed by * SSLEngine. */ private static final TestCase yesPskNoKexModes = new TestCase() { @Override public void execTest() throws Exception { try { System.out.println("\nTest: PSK = Yes, PSKEX = No"); processClientHello("TLS", CLIHELLO_YESPSK_NOPSKEXMODE); throw new Exception( "FAIL: Client Hello processed without expected error"); } catch (SSLHandshakeException sslhe) { System.out.println("PASS: Caught expected exception: " + sslhe); } } }; /** * Test case using a client hello asserting the pre_shared_key and * psk_key_exchange_modes extensions. */ private static final TestCase yesPskYesKexModes = new TestCase() { @Override public void execTest() throws Exception { System.out.println("\nTest: PSK = Yes, PSKEX = Yes"); processClientHello("TLS", CLIHELLO_YESPSK_YESPSKEXMODE); System.out.println("PASS"); } }; /** * Test case with a client hello asserting two named curves in the * supported_groups extension: sect163k1 and secp256r1. */ private static final TestCase supGroupsSect163k1 = new TestCase() { @Override public void execTest() throws Exception { System.out.println("\nTest: Use of non-default-enabled " + "Supported Group (sect163k1)"); processClientHello("TLS", CLIHELLO_SUPGRP_SECT163K1); System.out.println("PASS"); } }; /** * Send a ClientHello message to an SSLEngine instance configured as a * server. * * @param proto the protocol used to create the SSLContext. This will * default to "TLS" if null is passed in. * @param message the ClientHello as a complete TLS record. * * @throws Exception if any processing errors occur. The caller (TestCase) * is expected to deal with the exception in whatever way appropriate * for the test. */ private static void processClientHello(String proto, byte[] message) throws Exception { SSLEngine serverEng = makeServerEngine(proto, keyMgrFac, trustMgrFac); ByteBuffer sTOc = makePacketBuf(serverEng); SSLEngineResult serverResult; ByteBuffer cTOs = ByteBuffer.wrap(message); System.out.println("CLIENT-TO-SERVER\n" + dumpHexBytes(cTOs, 16, "\n", " ")); serverResult = serverEng.unwrap(cTOs, SERVOUTBUF); printResult("server unwrap: ", serverResult); runDelegatedTasks(serverResult, serverEng); serverEng.wrap(SERVOUTBUF, sTOc); } /** * Create a TrustManagerFactory from a given keystore. * * @param tsPath the path to the trust store file. * @param pass the password for the trust store. * * @return a new TrustManagerFactory built from the trust store provided. * * @throws GeneralSecurityException if any processing errors occur * with the Keystore instantiation or TrustManagerFactory creation. * @throws IOException if any loading error with the trust store occurs. */ private static TrustManagerFactory makeTrustManagerFactory(String tsPath, String pass) throws GeneralSecurityException, IOException { KeyStore ts = KeyStore.getInstance("JKS"); char[] passphrase = pass.toCharArray(); ts.load(new FileInputStream(tsPath), passphrase); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); return tmf; } /** * Create a KeyManagerFactory from a given keystore. * * @param ksPath the path to the keystore file. * @param pass the password for the keystore. * * @return a new TrustManagerFactory built from the keystore provided. * * @throws GeneralSecurityException if any processing errors occur * with the Keystore instantiation or KeyManagerFactory creation. * @throws IOException if any loading error with the keystore occurs */ private static KeyManagerFactory makeKeyManagerFactory(String ksPath, String pass) throws GeneralSecurityException, IOException { KeyStore ks = KeyStore.getInstance("JKS"); char[] passphrase = pass.toCharArray(); ks.load(new FileInputStream(ksPath), passphrase); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passphrase); return kmf; } /** * Create an SSLEngine instance from a given protocol specifier, * KeyManagerFactory and TrustManagerFactory. * * @param proto the protocol specifier for the SSLContext. This will * default to "TLS" if null is provided. * @param kmf an initialized KeyManagerFactory. May be null. * @param tmf an initialized TrustManagerFactory. May be null. * * @return an SSLEngine instance configured as a server and with client * authentication disabled. * * @throws GeneralSecurityException if any errors occur during the * creation of the SSLEngine. */ private static SSLEngine makeServerEngine(String proto, KeyManagerFactory kmf, TrustManagerFactory tmf) throws GeneralSecurityException { SSLContext ctx = SSLContext.getInstance(proto != null ? proto : "TLS"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLEngine ssle = ctx.createSSLEngine(); ssle.setUseClientMode(false); ssle.setNeedClientAuth(false); return ssle; } /** * Make a ByteBuffer sized for TLS records that can be used by an SSLEngine. * * @param engine the SSLEngine used to determine the packet buffer size. * * @return a ByteBuffer sized for TLS packets. */ private static ByteBuffer makePacketBuf(SSLEngine engine) { SSLSession sess = engine.getSession(); ByteBuffer packetBuf = ByteBuffer.allocate(sess.getPacketBufferSize()); return packetBuf; } /** * Runs any delegated tasks after unwrapping TLS records. * * @param result the most recent result from an unwrap operation on * an SSLEngine. * @param engine the SSLEngine used to unwrap the data. * * @throws Exception if any errors occur while running the delegated * tasks. */ private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) throws Exception { HandshakeStatus hsStatus = result.getHandshakeStatus(); if (hsStatus == HandshakeStatus.NEED_TASK) { Runnable runnable; while ((runnable = engine.getDelegatedTask()) != null) { System.out.println("\trunning delegated task..."); runnable.run(); } hsStatus = engine.getHandshakeStatus(); if (hsStatus == HandshakeStatus.NEED_TASK) { throw new Exception( "handshake shouldn't need additional tasks"); } System.out.println("\tnew HandshakeStatus: " + hsStatus); } } /** * Display the results of a wrap or unwrap operation from an SSLEngine. * * @param str a label to be prefixed to the result display. * @param result the result returned from the wrap/unwrap operation. */ private static void printResult(String str, SSLEngineResult result) { System.out.println("The format of the SSLEngineResult is: \n" + "\t\"getStatus() / getHandshakeStatus()\" +\n" + "\t\"bytesConsumed() / bytesProduced()\"\n"); HandshakeStatus hsStatus = result.getHandshakeStatus(); System.out.println(str + result.getStatus() + "/" + hsStatus + ", " + result.bytesConsumed() + "/" + result.bytesProduced() + " bytes"); if (hsStatus == HandshakeStatus.FINISHED) { System.out.println("\t...ready for application data"); } } /** * Dump the hex bytes of a buffer into string form. * * @param data The array of bytes to dump to stdout. * @param itemsPerLine The number of bytes to display per line * if the {@code lineDelim} character is blank then all bytes * will be printed on a single line. * @param lineDelim The delimiter between lines * @param itemDelim The delimiter between bytes * * @return The hexdump of the byte array */ private static String dumpHexBytes(byte[] data, int itemsPerLine, String lineDelim, String itemDelim) { return dumpHexBytes(ByteBuffer.wrap(data), itemsPerLine, lineDelim, itemDelim); } /** * Dump the hex bytes of a buffer into string form. * * @param data The ByteBuffer to dump to stdout. * @param itemsPerLine The number of bytes to display per line * if the {@code lineDelim} character is blank then all bytes * will be printed on a single line. * @param lineDelim The delimiter between lines * @param itemDelim The delimiter between bytes * * @return The hexdump of the byte array */ private static String dumpHexBytes(ByteBuffer data, int itemsPerLine, String lineDelim, String itemDelim) { StringBuilder sb = new StringBuilder(); if (data != null) { data.mark(); int i = 0; while (data.remaining() > 0) { if (i % itemsPerLine == 0 && i != 0) { sb.append(lineDelim); } sb.append(String.format("%02X", data.get())).append(itemDelim); i++; } data.reset(); } return sb.toString(); } }