blob: c147f732ec5765ada5375a43731e8a14b5638b36 [file] [log] [blame]
/*
* 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<String, TestCase> 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();
}
}