# HG changeset patch # User wetmore # Date 1452735361 28800 # Node ID 82de431119cd52d5b5a4722cdfc080f4b69effea # Parent 4a0926942b53eb9d1052906bc0506a01c9c44bf9 8145849: ALPN: getHandshakeApplicationProtocol() always return null Reviewed-by: wetmore, vinnie Contributed-by: amanda.jiang@oracle.com diff -r 4a0926942b53 -r 82de431119cd jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Wed Jan 13 10:25:41 2016 -0500 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Wed Jan 13 17:36:01 2016 -0800 @@ -2211,7 +2211,7 @@ @Override public synchronized String getHandshakeApplicationProtocol() { - if ((handshaker != null) && !handshaker.started()) { + if ((handshaker != null) && handshaker.started()) { return handshaker.getHandshakeApplicationProtocol(); } return null; diff -r 4a0926942b53 -r 82de431119cd jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Wed Jan 13 10:25:41 2016 -0500 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Wed Jan 13 17:36:01 2016 -0800 @@ -2598,7 +2598,7 @@ @Override public synchronized String getHandshakeApplicationProtocol() { - if ((handshaker != null) && !handshaker.started()) { + if ((handshaker != null) && handshaker.started()) { return handshaker.getHandshakeApplicationProtocol(); } return null; diff -r 4a0926942b53 -r 82de431119cd jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java Wed Jan 13 17:36:01 2016 -0800 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016, 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. + */ + +import java.net.Socket; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedKeyManager; + +public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager { + + static final String ERROR = "ERROR"; + X509ExtendedKeyManager akm; + String expectedAP; + + MyX509ExtendedKeyManager(X509ExtendedKeyManager akm) { + this.akm = akm; + } + + public MyX509ExtendedKeyManager( + X509ExtendedKeyManager akm, String expectedAP) { + this.akm = akm; + this.expectedAP = expectedAP; + + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return akm.getClientAliases(keyType, issuers); + } + + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + String nap = ((SSLSocket) socket).getHandshakeApplicationProtocol(); + checkALPN(nap); + + return akm.chooseClientAlias(keyType, issuers, socket); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return akm.getServerAliases(keyType, issuers); + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + String nap = ((SSLSocket) socket).getHandshakeApplicationProtocol(); + checkALPN(nap); + + return akm.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return akm.getCertificateChain(alias); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + return akm.getPrivateKey(alias); + } + + @Override + public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, + SSLEngine engine) { + String nap = engine.getHandshakeApplicationProtocol(); + checkALPN(nap); + + return akm.chooseEngineClientAlias(keyType, issuers, engine); + } + + @Override + public String chooseEngineServerAlias(String keyType, Principal[] issuers, + SSLEngine engine) { + String nap = engine.getHandshakeApplicationProtocol(); + checkALPN(nap); + + return akm.chooseEngineServerAlias(keyType, issuers, engine); + } + + private void checkALPN(String ap) { + + if (ERROR.equals(expectedAP)) { + throw new RuntimeException("Should not reach here"); + } + + System.out.println("Expected ALPN value: " + expectedAP + + " Got: " + ap); + + if (ap == null) { + throw new RuntimeException( + "ALPN should be negotiated, but null was received"); + } + if (expectedAP.equals("NONE")) { + if (!ap.isEmpty()) { + throw new RuntimeException("Expected no ALPN value"); + } else { + System.out.println("No ALPN value negotiated, as expected"); + } + } else if (!expectedAP.equals(ap)) { + throw new RuntimeException(expectedAP + + " ALPN value not available on negotiated connection"); + } + + } +} diff -r 4a0926942b53 -r 82de431119cd jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java --- a/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java Wed Jan 13 10:25:41 2016 -0500 +++ b/jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java Wed Jan 13 17:36:01 2016 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -26,8 +26,9 @@ /* * @test - * @bug 8051498 + * @bug 8051498 8145849 * @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension + * @compile MyX509ExtendedKeyManager.java * @run main/othervm SSLEngineAlpnTest h2 h2 h2 * @run main/othervm SSLEngineAlpnTest h2 h2,http/1.1 h2 * @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2,http/1.1 h2 @@ -162,7 +163,7 @@ throw new Exception("Invalid number of test parameters"); } - SSLEngineAlpnTest test = new SSLEngineAlpnTest(); + SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[2]); try { test.runTest(convert(args[0]), convert(args[1]), args[2]); } catch (SSLHandshakeException she) { @@ -179,7 +180,7 @@ /* * Create an initialized SSLContext to use for these tests. */ - public SSLEngineAlpnTest() throws Exception { + public SSLEngineAlpnTest(String expectedAP) throws Exception { KeyStore ks = KeyStore.getInstance("JKS"); KeyStore ts = KeyStore.getInstance("JKS"); @@ -192,12 +193,20 @@ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passphrase); + KeyManager [] kms = kmf.getKeyManagers(); + if (!(kms[0] instanceof X509ExtendedKeyManager)) { + throw new Exception("kms[0] not X509ExtendedKeyManager"); + } + + kms = new KeyManager[] { new MyX509ExtendedKeyManager( + (X509ExtendedKeyManager) kms[0], expectedAP) }; + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); SSLContext sslCtx = SSLContext.getInstance("TLS"); - sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + sslCtx.init(kms, tmf.getTrustManagers(), null); sslc = sslCtx; } @@ -327,6 +336,11 @@ return; } + if (engine.getHandshakeApplicationProtocol() != null) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null after the handshake is completed"); + } + String ap = engine.getApplicationProtocol(); System.out.println("Application Protocol: \"" + ap + "\""); @@ -384,6 +398,12 @@ sslp = clientEngine.getSSLParameters(); sslp.setApplicationProtocols(clientAPs); clientEngine.setSSLParameters(sslp); + + if ((clientEngine.getHandshakeApplicationProtocol() != null) || + (serverEngine.getHandshakeApplicationProtocol() != null)) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null before the handshake starts"); + } } /* diff -r 4a0926942b53 -r 82de431119cd jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java --- a/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java Wed Jan 13 10:25:41 2016 -0500 +++ b/jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.java Wed Jan 13 17:36:01 2016 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2016, 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 @@ -26,8 +26,9 @@ /* * @test - * @bug 8051498 + * @bug 8051498 8145849 * @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension + * @compile MyX509ExtendedKeyManager.java * @run main/othervm SSLSocketAlpnTest h2 h2 h2 * @run main/othervm SSLSocketAlpnTest h2 h2,http/1.1 h2 * @run main/othervm SSLSocketAlpnTest h2,http/1.1 h2,http/1.1 h2 @@ -40,6 +41,8 @@ * @author Brad Wetmore */ import java.io.*; +import java.security.KeyStore; + import javax.net.ssl.*; public class SSLSocketAlpnTest { @@ -65,6 +68,16 @@ static String trustStoreFile = "truststore"; static String passwd = "passphrase"; + static String keyFilename = System.getProperty("test.src", ".") + "/" + + pathToStores + "/" + keyStoreFile; + static String trustFilename = System.getProperty("test.src", ".") + "/" + + pathToStores + "/" + trustStoreFile; + + /* + * SSLContext + */ + SSLContext mySSLContext = null; + /* * Is the server ready to serve? */ @@ -82,7 +95,7 @@ /* * If the client or server is doing some kind of object creation * that the other side depends on, and that thread prematurely - * exits, you may experience a hang. The test harness will + * exits, you may experience a hang. The test harness will * terminate all hung threads after its timeout has expired, * currently 3 minutes by default, but you might try to be * smart about it.... @@ -95,10 +108,11 @@ * to avoid infinite hangs. */ void doServerSide() throws Exception { - SSLServerSocketFactory sslssf - = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + SSLServerSocketFactory sslssf = mySSLContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(serverPort); + // for both client/server to call into X509KM + sslServerSocket.setNeedClientAuth(true); serverPort = sslServerSocket.getLocalPort(); @@ -119,20 +133,30 @@ */ String[] suites = sslp.getCipherSuites(); sslp.setCipherSuites(suites); - sslp.setUseCipherSuitesOrder(true); // Set server side order + sslp.setUseCipherSuitesOrder(true); // Set server side order // Set the ALPN selection. sslp.setApplicationProtocols(serverAPs); sslSocket.setSSLParameters(sslp); + if (sslSocket.getHandshakeApplicationProtocol() != null) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null before the handshake starts"); + } + sslSocket.startHandshake(); + if (sslSocket.getHandshakeApplicationProtocol() != null) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null after the handshake is completed"); + } + String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol: \"" + ap + "\""); if (ap == null) { throw new Exception( - "Handshake was completed but null was received"); + "Handshake was completed but null was received"); } if (expectedAP.equals("NONE")) { if (!ap.isEmpty()) { @@ -141,8 +165,8 @@ System.out.println("No ALPN value negotiated, as expected"); } } else if (!expectedAP.equals(ap)) { - throw new Exception(expectedAP + - " ALPN value not available on negotiated connection"); + throw new Exception(expectedAP + + " ALPN value not available on negotiated connection"); } InputStream sslIS = sslSocket.getInputStream(); @@ -170,8 +194,7 @@ Thread.sleep(50); } - SSLSocketFactory sslsf - = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocketFactory sslsf = mySSLContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", serverPort); @@ -185,28 +208,35 @@ */ String[] suites = sslp.getCipherSuites(); sslp.setCipherSuites(suites); - sslp.setUseCipherSuitesOrder(true); // Set server side order + sslp.setUseCipherSuitesOrder(true); // Set server side order // Set the ALPN selection. sslp.setApplicationProtocols(clientAPs); sslSocket.setSSLParameters(sslp); + if (sslSocket.getHandshakeApplicationProtocol() != null) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null before the handshake starts"); + } + sslSocket.startHandshake(); + if (sslSocket.getHandshakeApplicationProtocol() != null) { + throw new Exception ("getHandshakeApplicationProtocol() should " + + "return null after the handshake is completed"); + } + /* * Check that the resulting connection meets our defined ALPN * criteria. If we were connecting to a non-JSSE implementation, * the server might have negotiated something we shouldn't accept. - * - * We were expecting H2 from server, let's make sure the - * conditions match. */ String ap = sslSocket.getApplicationProtocol(); System.out.println("Application Protocol: \"" + ap + "\""); if (ap == null) { throw new Exception( - "Handshake was completed but null was received"); + "Handshake was completed but null was received"); } if (expectedAP.equals("NONE")) { if (!ap.isEmpty()) { @@ -215,8 +245,8 @@ System.out.println("No ALPN value negotiated, as expected"); } } else if (!expectedAP.equals(ap)) { - throw new Exception(expectedAP + - " ALPN value not available on negotiated connection"); + throw new Exception(expectedAP + + " ALPN value not available on negotiated connection"); } InputStream sslIS = sslSocket.getInputStream(); @@ -240,17 +270,6 @@ volatile Exception clientException = null; public static void main(String[] args) throws Exception { - String keyFilename - = System.getProperty("test.src", ".") + "/" + pathToStores - + "/" + keyStoreFile; - String trustFilename - = System.getProperty("test.src", ".") + "/" + pathToStores - + "/" + trustStoreFile; - - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); if (debug) { System.setProperty("javax.net.debug", "all"); @@ -280,6 +299,39 @@ System.out.println("Test Passed."); } + SSLContext getSSLContext(String keyFilename, String trustFilename) + throws Exception { + SSLContext ctx = SSLContext.getInstance("TLS"); + + // Keystores + KeyStore keyKS = KeyStore.getInstance("JKS"); + keyKS.load(new FileInputStream(keyFilename), passwd.toCharArray()); + + KeyStore trustKS = KeyStore.getInstance("JKS"); + trustKS.load(new FileInputStream(trustFilename), passwd.toCharArray()); + + // Generate KeyManager and TrustManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyKS, passwd.toCharArray()); + + KeyManager[] kms = kmf.getKeyManagers(); + if (!(kms[0] instanceof X509ExtendedKeyManager)) { + throw new Exception("kms[0] not X509ExtendedKeyManager"); + } + + kms = new KeyManager[] { new MyX509ExtendedKeyManager( + (X509ExtendedKeyManager) kms[0], expectedAP) }; + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(trustKS); + TrustManager[] tms = tmf.getTrustManagers(); + + // initial SSLContext + ctx.init(kms, tms, null); + + return ctx; + } + /* * Convert a comma-separated list into an array of strings. */ @@ -309,6 +361,7 @@ */ SSLSocketAlpnTest() throws Exception { Exception startException = null; + mySSLContext = getSSLContext(keyFilename, trustFilename); try { if (separateServerThread) { startServer(true);