8145849: ALPN: getHandshakeApplicationProtocol() always return null
Reviewed-by: wetmore, vinnie
Contributed-by: amanda.jiang@oracle.com
--- 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;
--- 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;
--- /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");
+ }
+
+ }
+}
--- 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");
+ }
}
/*
--- 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);