8210989: RSASSA-PSS certificate cannot be selected for client auth on TLSv1.2
authorjnimeh
Tue, 16 Oct 2018 12:05:57 -0700
changeset 52153 3b17277860e7
parent 52152 bfdf2926cebc
child 52154 a4d4c609d70c
8210989: RSASSA-PSS certificate cannot be selected for client auth on TLSv1.2 Reviewed-by: xuelei
src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
src/java.base/share/classes/sun/security/ssl/HandshakeContext.java
src/java.base/share/classes/sun/security/ssl/X509Authentication.java
--- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java	Tue Oct 16 11:47:33 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java	Tue Oct 16 12:05:57 2018 -0700
@@ -31,7 +31,9 @@
 import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -675,44 +677,85 @@
             chc.peerRequestedSignatureSchemes = sss;
             chc.peerRequestedCertSignSchemes = sss;     // use the same schemes
             chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
+            chc.peerSupportedAuthorities = crm.getAuthorities();
 
-            X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
-            String clientAlias = null;
-            if (chc.conContext.transport instanceof SSLSocketImpl) {
-                clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
-                    crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
-            } else if (chc.conContext.transport instanceof SSLEngineImpl) {
-                clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
-                    crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
-            }
-
-            if (clientAlias == null) {
-                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                    SSLLogger.warning("No available client authentication");
-                }
+            // For TLS 1.2, we no longer use the certificate_types field
+            // from the CertificateRequest message to directly determine
+            // the SSLPossession.  Instead, the choosePossession method
+            // will use the accepted signature schemes in the message to
+            // determine the set of acceptable certificate types to select from.
+            SSLPossession pos = choosePossession(chc);
+            if (pos == null) {
                 return;
             }
 
-            PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
-            if (clientPrivateKey == null) {
+            chc.handshakePossessions.add(pos);
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+        }
+
+        private static SSLPossession choosePossession(HandshakeContext hc)
+                throws IOException {
+            if (hc.peerRequestedCertSignSchemes == null ||
+                    hc.peerRequestedCertSignSchemes.isEmpty()) {
                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                    SSLLogger.warning("No available client private key");
+                    SSLLogger.warning("No signature and hash algorithms " +
+                            "in CertificateRequest");
                 }
-                return;
+                return null;
             }
 
-            X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
-            if ((clientCerts == null) || (clientCerts.length == 0)) {
-                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
-                    SSLLogger.warning("No available client certificate");
+            Collection<String> checkedKeyTypes = new HashSet<>();
+            for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) {
+                if (checkedKeyTypes.contains(ss.keyAlgorithm)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unsupported authentication scheme: " + ss.name);
+                    }
+                    continue;
                 }
-                return;
+
+                // Don't select a signature scheme unless we will be able to
+                // produce a CertificateVerify message later
+                if (SignatureScheme.getPreferableAlgorithm(
+                        hc.peerRequestedSignatureSchemes,
+                        ss, hc.negotiatedProtocol) == null) {
+
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unable to produce CertificateVerify for " +
+                            "signature scheme: " + ss.name);
+                    }
+                    checkedKeyTypes.add(ss.keyAlgorithm);
+                    continue;
+                }
+
+                SSLAuthentication ka = X509Authentication.valueOf(ss);
+                if (ka == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unsupported authentication scheme: " + ss.name);
+                    }
+                    checkedKeyTypes.add(ss.keyAlgorithm);
+                    continue;
+                }
+
+                SSLPossession pos = ka.createPossession(hc);
+                if (pos == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unavailable authentication scheme: " + ss.name);
+                    }
+                    continue;
+                }
+
+                return pos;
             }
 
-            chc.handshakePossessions.add(
-                    new X509Possession(clientPrivateKey, clientCerts));
-            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
-                    SSLHandshake.CERTIFICATE_VERIFY);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.warning("No available authentication scheme");
+            }
+            return null;
         }
     }
 
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Tue Oct 16 11:47:33 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Tue Oct 16 12:05:57 2018 -0700
@@ -43,6 +43,7 @@
 import javax.crypto.SecretKey;
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLHandshakeException;
+import javax.security.auth.x500.X500Principal;
 import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
 import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
 import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
@@ -136,6 +137,9 @@
     List<SignatureScheme>                   peerRequestedSignatureSchemes;
     List<SignatureScheme>                   peerRequestedCertSignSchemes;
 
+    // Known authorities
+    X500Principal[]                         peerSupportedAuthorities = null;
+
     // SupportedGroups
     List<NamedGroup>                        clientRequestedNamedGroups;
 
--- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Tue Oct 16 11:47:33 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Tue Oct 16 12:05:57 2018 -0700
@@ -170,7 +170,7 @@
             return null;
         }
 
-        // Used by TLS 1.3 only.
+        // Used by TLS 1.2 and TLS 1.3.
         private SSLPossession createClientPossession(
                 ClientHandshakeContext chc, String keyType) {
             X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
@@ -178,11 +178,13 @@
             if (chc.conContext.transport instanceof SSLSocketImpl) {
                 clientAlias = km.chooseClientAlias(
                         new String[] { keyType },
-                        null, (SSLSocket)chc.conContext.transport);
+                        chc.peerSupportedAuthorities,
+                        (SSLSocket)chc.conContext.transport);
             } else if (chc.conContext.transport instanceof SSLEngineImpl) {
                 clientAlias = km.chooseEngineClientAlias(
                         new String[] { keyType },
-                        null, (SSLEngine)chc.conContext.transport);
+                        chc.peerSupportedAuthorities,
+                        (SSLEngine)chc.conContext.transport);
             }
 
             if (clientAlias == null) {