Path: blob/master/test/jdk/javax/net/ssl/ALPN/SSLEngineAlpnTest.java
41152 views
/*1* Copyright (c) 2003, 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// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode.2526/*27* @test28* @bug 8051498 8145849 817028229* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension30* @compile MyX509ExtendedKeyManager.java31*32* @run main/othervm SSLEngineAlpnTest h2 UNUSED h2 h233* @run main/othervm SSLEngineAlpnTest h2 UNUSED h2,http/1.1 h234* @run main/othervm SSLEngineAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h235* @run main/othervm SSLEngineAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.136* @run main/othervm SSLEngineAlpnTest h4,h3,h2 UNUSED h1,h2 h237* @run main/othervm SSLEngineAlpnTest EMPTY UNUSED h2,http/1.1 NONE38* @run main/othervm SSLEngineAlpnTest h2 UNUSED EMPTY NONE39* @run main/othervm SSLEngineAlpnTest H2 UNUSED h2 ERROR40* @run main/othervm SSLEngineAlpnTest h2 UNUSED http/1.1 ERROR41*42* @run main/othervm SSLEngineAlpnTest UNUSED h2 h2 h243* @run main/othervm SSLEngineAlpnTest UNUSED h2 h2,http/1.1 h244* @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1,h2 h245* @run main/othervm SSLEngineAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.146* @run main/othervm SSLEngineAlpnTest UNUSED EMPTY h2,http/1.1 NONE47* @run main/othervm SSLEngineAlpnTest UNUSED h2 EMPTY NONE48* @run main/othervm SSLEngineAlpnTest UNUSED H2 h2 ERROR49* @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1 ERROR50*51* @run main/othervm SSLEngineAlpnTest h2 h2 h2 h252* @run main/othervm SSLEngineAlpnTest H2 h2 h2,http/1.1 h253* @run main/othervm SSLEngineAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.154* @run main/othervm SSLEngineAlpnTest http/1.1,h2 h2 h2,http/1.1 h255* @run main/othervm SSLEngineAlpnTest EMPTY h2 h2 h256* @run main/othervm SSLEngineAlpnTest h2,http/1.1 EMPTY http/1.1 NONE57* @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2 EMPTY NONE58* @run main/othervm SSLEngineAlpnTest UNUSED UNUSED http/1.1,h2 NONE59* @run main/othervm SSLEngineAlpnTest h2 h2 http/1.1 ERROR60* @run main/othervm SSLEngineAlpnTest h2,http/1.1 H2 http/1.1 ERROR61*/62/**63* A simple SSLEngine-based client/server that demonstrates the proposed API64* changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).65*66* Usage:67* java SSLEngineAlpnTest <server-APs> <callback-AP> <client-APs> <result>68*69* where:70* EMPTY indicates that ALPN is disabled71* UNUSED indicates that no ALPN values are supplied (server-side only)72* ERROR indicates that an exception is expected73* NONE indicates that no ALPN is expected74*75* This example is based on our standard SSLEngineTemplate.76*77* The immediate consumer of ALPN will be HTTP/2 (RFC 7540), aka H2. The H2 IETF78* Working Group wanted to use TLSv1.3+ as the secure transport mechanism, but79* TLSv1.3 wasn't ready. The H2 folk agreed to a compromise that only TLSv1.2+80* can be used, and that if TLSv1.2 was selected, non-TLSv.1.3-approved81* ciphersuites would be blacklisted and their use discouraged.82*83* In order to support connections that might negotiate either HTTP/1.1 and H2,84* the guidance from the IETF Working Group is that the H2 ciphersuites be85* prioritized/tried first.86*/8788/*89* The original SSLEngineTemplate comments follow.90*91* A SSLEngine usage example which simplifies the presentation92* by removing the I/O and multi-threading concerns.93*94* The test creates two SSLEngines, simulating a client and server.95* The "transport" layer consists two byte buffers: think of them96* as directly connected pipes.97*98* Note, this is a *very* simple example: real code will be much more99* involved. For example, different threading and I/O models could be100* used, transport mechanisms could close unexpectedly, and so on.101*102* When this application runs, notice that several messages103* (wrap/unwrap) pass before any application data is consumed or104* produced. (For more information, please see the SSL/TLS105* specifications.) There may several steps for a successful handshake,106* so it's typical to see the following series of operations:107*108* client server message109* ====== ====== =======110* wrap() ... ClientHello111* ... unwrap() ClientHello112* ... wrap() ServerHello/Certificate113* unwrap() ... ServerHello/Certificate114* wrap() ... ClientKeyExchange115* wrap() ... ChangeCipherSpec116* wrap() ... Finished117* ... unwrap() ClientKeyExchange118* ... unwrap() ChangeCipherSpec119* ... unwrap() Finished120* ... wrap() ChangeCipherSpec121* ... wrap() Finished122* unwrap() ... ChangeCipherSpec123* unwrap() ... Finished124*/125import javax.net.ssl.*;126import javax.net.ssl.SSLEngineResult.*;127import java.io.*;128import java.security.*;129import java.nio.*;130import java.util.Arrays;131132public class SSLEngineAlpnTest {133134/*135* Enables logging of the SSLEngine operations.136*/137private static final boolean logging = true;138139/*140* Enables the JSSE system debugging system property:141*142* -Djavax.net.debug=all143*144* This gives a lot of low-level information about operations underway,145* including specific handshake messages, and might be best examined146* after gaining some familiarity with this application.147*/148private static final boolean debug = false;149150private static boolean hasServerAPs; // whether server APs are present151private static boolean hasCallback; // whether a callback is present152153private final SSLContext sslc;154155private SSLEngine clientEngine; // client Engine156private ByteBuffer clientOut; // write side of clientEngine157private ByteBuffer clientIn; // read side of clientEngine158159private SSLEngine serverEngine; // server Engine160private ByteBuffer serverOut; // write side of serverEngine161private ByteBuffer serverIn; // read side of serverEngine162163/*164* For data transport, this example uses local ByteBuffers. This165* isn't really useful, but the purpose of this example is to show166* SSLEngine concepts, not how to do network transport.167*/168private ByteBuffer cTOs; // "reliable" transport client->server169private ByteBuffer sTOc; // "reliable" transport server->client170171/*172* The following is to set up the keystores.173*/174private static final String pathToStores = "../etc";175private static final String keyStoreFile = "keystore";176private static final String trustStoreFile = "truststore";177private static final String passwd = "passphrase";178179private static final String keyFilename180= System.getProperty("test.src", ".") + "/" + pathToStores181+ "/" + keyStoreFile;182private static final String trustFilename183= System.getProperty("test.src", ".") + "/" + pathToStores184+ "/" + trustStoreFile;185186/*187* Main entry point for this test.188*/189public static void main(String args[]) throws Exception {190if (debug) {191System.setProperty("javax.net.debug", "all");192}193System.setProperty("jdk.tls.acknowledgeCloseNotify", "true");194System.out.println("Test args: " + Arrays.toString(args));195196// Validate parameters197if (args.length != 4) {198throw new Exception("Invalid number of test parameters");199}200201hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?202hasCallback = !args[1].equals("UNUSED"); // is callback being used?203204SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[3]);205try {206test.runTest(convert(args[0]), args[1], convert(args[2]), args[3]);207} catch (SSLHandshakeException she) {208if (args[3].equals("ERROR")) {209System.out.println("Caught the expected exception: " + she);210} else {211throw she;212}213}214215System.out.println("Test Passed.");216}217218/*219* Create an initialized SSLContext to use for these tests.220*/221public SSLEngineAlpnTest(String expectedAP) throws Exception {222223KeyStore ks = KeyStore.getInstance("JKS");224KeyStore ts = KeyStore.getInstance("JKS");225226char[] passphrase = "passphrase".toCharArray();227228ks.load(new FileInputStream(keyFilename), passphrase);229ts.load(new FileInputStream(trustFilename), passphrase);230231KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");232kmf.init(ks, passphrase);233234KeyManager [] kms = kmf.getKeyManagers();235if (!(kms[0] instanceof X509ExtendedKeyManager)) {236throw new Exception("kms[0] not X509ExtendedKeyManager");237}238239kms = new KeyManager[] { new MyX509ExtendedKeyManager(240(X509ExtendedKeyManager) kms[0], expectedAP,241!hasCallback && hasServerAPs) };242243TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");244tmf.init(ts);245246SSLContext sslCtx = SSLContext.getInstance("TLS");247248sslCtx.init(kms, tmf.getTrustManagers(), null);249250sslc = sslCtx;251}252253/*254* Convert a comma-separated list into an array of strings.255*/256private static String[] convert(String list) {257if (list.equals("UNUSED")) {258return null;259}260261if (list.equals("EMPTY")) {262return new String[0];263}264265String[] strings;266if (list.indexOf(',') > 0) {267strings = list.split(",");268} else {269strings = new String[]{ list };270}271272return strings;273}274275/*276* Run the test.277*278* Sit in a tight loop, both engines calling wrap/unwrap regardless279* of whether data is available or not. We do this until both engines280* report back they are closed.281*282* The main loop handles all of the I/O phases of the SSLEngine's283* lifetime:284*285* initial handshaking286* application data transfer287* engine closing288*289* One could easily separate these phases into separate290* sections of code.291*/292private void runTest(String[] serverAPs, String callbackAP,293String[] clientAPs, String expectedAP) throws Exception {294295boolean dataDone = false;296297createSSLEngines(serverAPs, callbackAP, clientAPs);298createBuffers();299300SSLEngineResult clientResult; // results from client's last operation301SSLEngineResult serverResult; // results from server's last operation302303/*304* Examining the SSLEngineResults could be much more involved,305* and may alter the overall flow of the application.306*307* For example, if we received a BUFFER_OVERFLOW when trying308* to write to the output pipe, we could reallocate a larger309* pipe, but instead we wait for the peer to drain it.310*/311while (!isEngineClosed(clientEngine)312|| !isEngineClosed(serverEngine)) {313314log("================");315316clientResult = clientEngine.wrap(clientOut, cTOs);317log("client wrap: ", clientResult);318runDelegatedTasks(clientResult, clientEngine);319checkAPResult(clientEngine, clientResult, expectedAP);320321serverResult = serverEngine.wrap(serverOut, sTOc);322log("server wrap: ", serverResult);323runDelegatedTasks(serverResult, serverEngine);324checkAPResult(serverEngine, serverResult, expectedAP);325326cTOs.flip();327sTOc.flip();328329log("----");330331clientResult = clientEngine.unwrap(sTOc, clientIn);332log("client unwrap: ", clientResult);333runDelegatedTasks(clientResult, clientEngine);334checkAPResult(clientEngine, clientResult, expectedAP);335336serverResult = serverEngine.unwrap(cTOs, serverIn);337log("server unwrap: ", serverResult);338runDelegatedTasks(serverResult, serverEngine);339checkAPResult(serverEngine, serverResult, expectedAP);340341cTOs.compact();342sTOc.compact();343344/*345* After we've transfered all application data between the client346* and server, we close the clientEngine's outbound stream.347* This generates a close_notify handshake message, which the348* server engine receives and responds by closing itself.349*/350if (!dataDone && (clientOut.limit() == serverIn.position())351&& (serverOut.limit() == clientIn.position())) {352353/*354* A sanity check to ensure we got what was sent.355*/356checkTransfer(serverOut, clientIn);357checkTransfer(clientOut, serverIn);358359log("\tClosing clientEngine's *OUTBOUND*...");360clientEngine.closeOutbound();361// serverEngine.closeOutbound();362dataDone = true;363}364}365}366367/*368* Check that the resulting connection meets our defined ALPN369* criteria. If we were connecting to a non-JSSE implementation,370* the server might have negotiated something we shouldn't accept.371*372* If we were expecting an ALPN value from server, let's make sure373* the conditions match.374*/375private static void checkAPResult(SSLEngine engine, SSLEngineResult result,376String expectedAP) throws Exception {377378if (result.getHandshakeStatus() != HandshakeStatus.FINISHED) {379return;380}381382if (engine.getHandshakeApplicationProtocol() != null) {383throw new Exception ("getHandshakeApplicationProtocol() should "384+ "return null after the handshake is completed");385}386387String ap = engine.getApplicationProtocol();388System.out.println("Application Protocol: \"" + ap + "\"");389390if (ap == null) {391throw new Exception(392"Handshake was completed but null was received");393}394if (expectedAP.equals("NONE")) {395if (!ap.isEmpty()) {396throw new Exception("Expected no ALPN value");397} else {398System.out.println("No ALPN value negotiated, as expected");399}400} else if (!expectedAP.equals(ap)) {401throw new Exception(expectedAP +402" ALPN value not available on negotiated connection");403}404}405406/*407* Using the SSLContext created during object creation,408* create/configure the SSLEngines we'll use for this test.409*/410private void createSSLEngines(String[] serverAPs, String callbackAP,411String[] clientAPs) throws Exception {412/*413* Configure the serverEngine to act as a server in the SSL/TLS414* handshake. Also, require SSL client authentication.415*/416serverEngine = sslc.createSSLEngine();417serverEngine.setUseClientMode(false);418419SSLParameters sslp = serverEngine.getSSLParameters();420421sslp.setNeedClientAuth(true);422423/*424* The default ciphersuite ordering from the SSLContext may not425* reflect "h2" ciphersuites as being preferred, additionally the426* client may not send them in an appropriate order. We could resort427* the suite list if so desired.428*/429String[] suites = sslp.getCipherSuites();430sslp.setCipherSuites(suites);431if (serverAPs != null) {432sslp.setApplicationProtocols(serverAPs);433}434sslp.setUseCipherSuitesOrder(true); // Set server side order435436serverEngine.setSSLParameters(sslp);437438// check that no callback has been registered439if (serverEngine.getHandshakeApplicationProtocolSelector() != null) {440throw new Exception("getHandshakeApplicationProtocolSelector() " +441"should return null");442}443444if (hasCallback) {445serverEngine.setHandshakeApplicationProtocolSelector(446(sslEngine, clientProtocols) -> {447return callbackAP.equals("EMPTY") ? "" : callbackAP;448});449450// check that the callback can be retrieved451if (serverEngine.getHandshakeApplicationProtocolSelector()452== null) {453throw new Exception("getHandshakeApplicationProtocolSelector()"454+ " should return non-null");455}456}457458/*459* Similar to above, but using client mode instead.460*/461clientEngine = sslc.createSSLEngine("client", 80);462clientEngine.setUseClientMode(true);463sslp = clientEngine.getSSLParameters();464if (clientAPs != null) {465sslp.setApplicationProtocols(clientAPs);466}467clientEngine.setSSLParameters(sslp);468469if ((clientEngine.getHandshakeApplicationProtocol() != null) ||470(serverEngine.getHandshakeApplicationProtocol() != null)) {471throw new Exception ("getHandshakeApplicationProtocol() should "472+ "return null before the handshake starts");473}474}475476/*477* Create and size the buffers appropriately.478*/479private void createBuffers() {480481/*482* We'll assume the buffer sizes are the same483* between client and server.484*/485SSLSession session = clientEngine.getSession();486int appBufferMax = session.getApplicationBufferSize();487int netBufferMax = session.getPacketBufferSize();488489/*490* We'll make the input buffers a bit bigger than the max needed491* size, so that unwrap()s following a successful data transfer492* won't generate BUFFER_OVERFLOWS.493*494* We'll use a mix of direct and indirect ByteBuffers for495* tutorial purposes only. In reality, only use direct496* ByteBuffers when they give a clear performance enhancement.497*/498clientIn = ByteBuffer.allocate(appBufferMax + 50);499serverIn = ByteBuffer.allocate(appBufferMax + 50);500501cTOs = ByteBuffer.allocateDirect(netBufferMax);502sTOc = ByteBuffer.allocateDirect(netBufferMax);503504clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());505serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());506}507508/*509* If the result indicates that we have outstanding tasks to do,510* go ahead and run them in this thread.511*/512private static void runDelegatedTasks(SSLEngineResult result,513SSLEngine engine) throws Exception {514515if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {516Runnable runnable;517while ((runnable = engine.getDelegatedTask()) != null) {518log("\trunning delegated task...");519runnable.run();520}521HandshakeStatus hsStatus = engine.getHandshakeStatus();522if (hsStatus == HandshakeStatus.NEED_TASK) {523throw new Exception(524"handshake shouldn't need additional tasks");525}526log("\tnew HandshakeStatus: " + hsStatus);527}528}529530private static boolean isEngineClosed(SSLEngine engine) {531return (engine.isOutboundDone() && engine.isInboundDone());532}533534/*535* Simple check to make sure everything came across as expected.536*/537private static void checkTransfer(ByteBuffer a, ByteBuffer b)538throws Exception {539a.flip();540b.flip();541542if (!a.equals(b)) {543throw new Exception("Data didn't transfer cleanly");544} else {545log("\tData transferred cleanly");546}547548a.position(a.limit());549b.position(b.limit());550a.limit(a.capacity());551b.limit(b.capacity());552}553554/*555* Logging code556*/557private static boolean resultOnce = true;558559private static void log(String str, SSLEngineResult result) {560if (!logging) {561return;562}563if (resultOnce) {564resultOnce = false;565System.out.println("The format of the SSLEngineResult is: \n"566+ "\t\"getStatus() / getHandshakeStatus()\" +\n"567+ "\t\"bytesConsumed() / bytesProduced()\"\n");568}569HandshakeStatus hsStatus = result.getHandshakeStatus();570log(str571+ result.getStatus() + "/" + hsStatus + ", "572+ result.bytesConsumed() + "/" + result.bytesProduced()573+ " bytes");574if (hsStatus == HandshakeStatus.FINISHED) {575log("\t...ready for application data");576}577}578579private static void log(String str) {580if (logging) {581System.out.println(str);582}583}584}585586587