8145849: ALPN: getHandshakeApplicationProtocol() always return null
authorwetmore
Wed, 13 Jan 2016 17:36:01 -0800
changeset 34958 82de431119cd
parent 34957 4a0926942b53
child 34959 4e7903220c89
8145849: ALPN: getHandshakeApplicationProtocol() always return null Reviewed-by: wetmore, vinnie Contributed-by: amanda.jiang@oracle.com
jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
jdk/test/javax/net/ssl/ALPN/MyX509ExtendedKeyManager.java
jdk/test/javax/net/ssl/ALPN/SSLEngineAlpnTest.java
jdk/test/javax/net/ssl/ALPN/SSLSocketAlpnTest.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;
--- 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);