| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.ByteArrayInputStream;  | 
 | 
import java.io.IOException;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.security.PublicKey;  | 
 | 
import java.security.cert.CertPathValidatorException;  | 
 | 
import java.security.cert.CertPathValidatorException.BasicReason;  | 
 | 
import java.security.cert.CertPathValidatorException.Reason;  | 
 | 
import java.security.cert.CertificateEncodingException;  | 
 | 
import java.security.cert.CertificateException;  | 
 | 
import java.security.cert.CertificateFactory;  | 
 | 
import java.security.cert.CertificateParsingException;  | 
 | 
import java.security.cert.X509Certificate;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Arrays;  | 
 | 
import java.util.Collection;  | 
 | 
import java.util.Collections;  | 
 | 
import java.util.HashSet;  | 
 | 
import java.util.LinkedList;  | 
 | 
import java.util.List;  | 
 | 
import java.util.Locale;  | 
 | 
import javax.net.ssl.SSLEngine;  | 
 | 
import javax.net.ssl.SSLException;  | 
 | 
import javax.net.ssl.SSLProtocolException;  | 
 | 
import javax.net.ssl.SSLSocket;  | 
 | 
import javax.net.ssl.X509ExtendedTrustManager;  | 
 | 
import javax.net.ssl.X509TrustManager;  | 
 | 
import javax.security.auth.x500.X500Principal;  | 
 | 
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;  | 
 | 
import sun.security.ssl.ClientHello.ClientHelloMessage;  | 
 | 
import sun.security.ssl.SSLHandshake.HandshakeMessage;  | 
 | 
import sun.security.ssl.X509Authentication.X509Credentials;  | 
 | 
import sun.security.ssl.X509Authentication.X509Possession;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class CertificateMessage { | 
 | 
    static final SSLConsumer t12HandshakeConsumer =  | 
 | 
        new T12CertificateConsumer();  | 
 | 
    static final HandshakeProducer t12HandshakeProducer =  | 
 | 
        new T12CertificateProducer();  | 
 | 
 | 
 | 
    static final SSLConsumer t13HandshakeConsumer =  | 
 | 
        new T13CertificateConsumer();  | 
 | 
    static final HandshakeProducer t13HandshakeProducer =  | 
 | 
        new T13CertificateProducer();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static final class T12CertificateMessage extends HandshakeMessage { | 
 | 
        final List<byte[]> encodedCertChain;  | 
 | 
 | 
 | 
        T12CertificateMessage(HandshakeContext handshakeContext,  | 
 | 
                X509Certificate[] certChain) throws SSLException { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            List<byte[]> encodedCerts = new ArrayList<>(certChain.length);  | 
 | 
            for (X509Certificate cert : certChain) { | 
 | 
                try { | 
 | 
                    encodedCerts.add(cert.getEncoded());  | 
 | 
                } catch (CertificateEncodingException cee) { | 
 | 
                      | 
 | 
                    throw handshakeContext.conContext.fatal(  | 
 | 
                            Alert.INTERNAL_ERROR,  | 
 | 
                            "Could not encode certificate (" + | 
 | 
                            cert.getSubjectX500Principal() + ")", cee);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            this.encodedCertChain = encodedCerts;  | 
 | 
        }  | 
 | 
 | 
 | 
        T12CertificateMessage(HandshakeContext handshakeContext,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            int listLen = Record.getInt24(m);  | 
 | 
            if (listLen > m.remaining()) { | 
 | 
                throw handshakeContext.conContext.fatal(  | 
 | 
                    Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Error parsing certificate message:no sufficient data");  | 
 | 
            }  | 
 | 
            if (listLen > 0) { | 
 | 
                List<byte[]> encodedCerts = new LinkedList<>();  | 
 | 
                while (listLen > 0) { | 
 | 
                    byte[] encodedCert = Record.getBytes24(m);  | 
 | 
                    listLen -= (3 + encodedCert.length);  | 
 | 
                    encodedCerts.add(encodedCert);  | 
 | 
                    if (encodedCerts.size() > SSLConfiguration.maxCertificateChainLength) { | 
 | 
                        throw new SSLProtocolException(  | 
 | 
                                "The certificate chain length (" | 
 | 
                                + encodedCerts.size()  | 
 | 
                                + ") exceeds the maximum allowed length (" | 
 | 
                                + SSLConfiguration.maxCertificateChainLength  | 
 | 
                                + ")");  | 
 | 
                    }  | 
 | 
 | 
 | 
                }  | 
 | 
                this.encodedCertChain = encodedCerts;  | 
 | 
            } else { | 
 | 
                this.encodedCertChain = Collections.emptyList();  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public SSLHandshake handshakeType() { | 
 | 
            return SSLHandshake.CERTIFICATE;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public int messageLength() { | 
 | 
            int msgLen = 3;  | 
 | 
            for (byte[] encodedCert : encodedCertChain) { | 
 | 
                msgLen += (encodedCert.length + 3);  | 
 | 
            }  | 
 | 
 | 
 | 
            return msgLen;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void send(HandshakeOutStream hos) throws IOException { | 
 | 
            int listLen = 0;  | 
 | 
            for (byte[] encodedCert : encodedCertChain) { | 
 | 
                listLen += (encodedCert.length + 3);  | 
 | 
            }  | 
 | 
 | 
 | 
            hos.putInt24(listLen);  | 
 | 
            for (byte[] encodedCert : encodedCertChain) { | 
 | 
                hos.putBytes24(encodedCert);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            if (encodedCertChain.isEmpty()) { | 
 | 
                return "\"Certificates\": <empty list>";  | 
 | 
            }  | 
 | 
 | 
 | 
            Object[] x509Certs = new Object[encodedCertChain.size()];  | 
 | 
            try { | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                int i = 0;  | 
 | 
                for (byte[] encodedCert : encodedCertChain) { | 
 | 
                    Object obj;  | 
 | 
                    try { | 
 | 
                        obj = (X509Certificate)cf.generateCertificate(  | 
 | 
                                    new ByteArrayInputStream(encodedCert));  | 
 | 
                    } catch (CertificateException ce) { | 
 | 
                        obj = encodedCert;  | 
 | 
                    }  | 
 | 
                    x509Certs[i++] = obj;  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                  | 
 | 
                int i = 0;  | 
 | 
                for (byte[] encodedCert : encodedCertChain) { | 
 | 
                    x509Certs[i++] = encodedCert;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                    "\"Certificates\": [\n" +  | 
 | 
                    "{0}\n" + | 
 | 
                    "]",  | 
 | 
                    Locale.ENGLISH);  | 
 | 
            Object[] messageFields = { | 
 | 
                SSLLogger.toString(x509Certs)  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class T12CertificateProducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private T12CertificateProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            HandshakeContext hc = (HandshakeContext)context;  | 
 | 
            if (hc.sslConfig.isClientMode) { | 
 | 
                return onProduceCertificate(  | 
 | 
                        (ClientHandshakeContext)context, message);  | 
 | 
            } else { | 
 | 
                return onProduceCertificate(  | 
 | 
                        (ServerHandshakeContext)context, message);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private byte[] onProduceCertificate(ServerHandshakeContext shc,  | 
 | 
                SSLHandshake.HandshakeMessage message) throws IOException { | 
 | 
            X509Possession x509Possession = null;  | 
 | 
            for (SSLPossession possession : shc.handshakePossessions) { | 
 | 
                if (possession instanceof X509Possession) { | 
 | 
                    x509Possession = (X509Possession)possession;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (x509Possession == null) {        | 
 | 
                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                    "No expected X.509 certificate for server authentication");  | 
 | 
            }  | 
 | 
 | 
 | 
            shc.handshakeSession.setLocalPrivateKey(  | 
 | 
                    x509Possession.popPrivateKey);  | 
 | 
            shc.handshakeSession.setLocalCertificates(x509Possession.popCerts);  | 
 | 
            T12CertificateMessage cm =  | 
 | 
                    new T12CertificateMessage(shc, x509Possession.popCerts);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Produced server Certificate handshake message", cm);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            cm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        private byte[] onProduceCertificate(ClientHandshakeContext chc,  | 
 | 
                SSLHandshake.HandshakeMessage message) throws IOException { | 
 | 
            X509Possession x509Possession = null;  | 
 | 
            for (SSLPossession possession : chc.handshakePossessions) { | 
 | 
                if (possession instanceof X509Possession) { | 
 | 
                    x509Possession = (X509Possession)possession;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            // Report to the server if no appropriate cert was found.  For  | 
 | 
            // SSL 3.0, send a no_certificate alert;  TLS 1.0/1.1/1.2 uses  | 
 | 
              | 
 | 
            if (x509Possession == null) { | 
 | 
                if (chc.negotiatedProtocol.useTLS10PlusSpec()) { | 
 | 
                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                        SSLLogger.fine(  | 
 | 
                            "No X.509 certificate for client authentication, " +  | 
 | 
                            "use empty Certificate message instead");  | 
 | 
                    }  | 
 | 
 | 
 | 
                    x509Possession =  | 
 | 
                            new X509Possession(null, new X509Certificate[0]);  | 
 | 
                } else { | 
 | 
                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                        SSLLogger.fine(  | 
 | 
                            "No X.509 certificate for client authentication, " +  | 
 | 
                            "send a no_certificate alert");  | 
 | 
                    }  | 
 | 
 | 
 | 
                    chc.conContext.warning(Alert.NO_CERTIFICATE);  | 
 | 
                    return null;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.handshakeSession.setLocalPrivateKey(  | 
 | 
                    x509Possession.popPrivateKey);  | 
 | 
            if (x509Possession.popCerts != null &&  | 
 | 
                    x509Possession.popCerts.length != 0) { | 
 | 
                chc.handshakeSession.setLocalCertificates(  | 
 | 
                        x509Possession.popCerts);  | 
 | 
            } else { | 
 | 
                chc.handshakeSession.setLocalCertificates(null);  | 
 | 
            }  | 
 | 
            T12CertificateMessage cm =  | 
 | 
                    new T12CertificateMessage(chc, x509Possession.popCerts);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Produced client Certificate handshake message", cm);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            cm.write(chc.handshakeOutput);  | 
 | 
            chc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static final  | 
 | 
            class T12CertificateConsumer implements SSLConsumer { | 
 | 
          | 
 | 
        private T12CertificateConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer message) throws IOException { | 
 | 
              | 
 | 
            HandshakeContext hc = (HandshakeContext)context;  | 
 | 
 | 
 | 
              | 
 | 
            hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);  | 
 | 
 | 
 | 
            T12CertificateMessage cm = new T12CertificateMessage(hc, message);  | 
 | 
            if (hc.sslConfig.isClientMode) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Consuming server Certificate handshake message", cm);  | 
 | 
                }  | 
 | 
                onCertificate((ClientHandshakeContext)context, cm);  | 
 | 
            } else { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Consuming client Certificate handshake message", cm);  | 
 | 
                }  | 
 | 
                onCertificate((ServerHandshakeContext)context, cm);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onCertificate(ServerHandshakeContext shc,  | 
 | 
                T12CertificateMessage certificateMessage )throws IOException { | 
 | 
            List<byte[]> encodedCerts = certificateMessage.encodedCertChain;  | 
 | 
            if (encodedCerts == null || encodedCerts.isEmpty()) { | 
 | 
                // For empty Certificate messages, we should not expect  | 
 | 
                  | 
 | 
                shc.handshakeConsumers.remove(  | 
 | 
                        SSLHandshake.CERTIFICATE_VERIFY.id);  | 
 | 
                if (shc.sslConfig.clientAuthType !=  | 
 | 
                        ClientAuthType.CLIENT_AUTH_REQUESTED) { | 
 | 
                      | 
 | 
                    throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                        "Empty server certificate chain");  | 
 | 
                } else { | 
 | 
                    return;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            X509Certificate[] x509Certs =  | 
 | 
                    new X509Certificate[encodedCerts.size()];  | 
 | 
            try { | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                int i = 0;  | 
 | 
                for (byte[] encodedCert : encodedCerts) { | 
 | 
                    x509Certs[i++] = (X509Certificate)cf.generateCertificate(  | 
 | 
                                    new ByteArrayInputStream(encodedCert));  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Failed to parse server certificates", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            checkClientCerts(shc, x509Certs);  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            shc.handshakeCredentials.add(  | 
 | 
                new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));  | 
 | 
            shc.handshakeSession.setPeerCertificates(x509Certs);  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onCertificate(ClientHandshakeContext chc,  | 
 | 
                T12CertificateMessage certificateMessage) throws IOException { | 
 | 
            List<byte[]> encodedCerts = certificateMessage.encodedCertChain;  | 
 | 
            if (encodedCerts == null || encodedCerts.isEmpty()) { | 
 | 
                throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Empty server certificate chain");  | 
 | 
            }  | 
 | 
 | 
 | 
            X509Certificate[] x509Certs =  | 
 | 
                    new X509Certificate[encodedCerts.size()];  | 
 | 
            try { | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                int i = 0;  | 
 | 
                for (byte[] encodedCert : encodedCerts) { | 
 | 
                    x509Certs[i++] = (X509Certificate)cf.generateCertificate(  | 
 | 
                                    new ByteArrayInputStream(encodedCert));  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Failed to parse server certificates", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            // Allow server certificate change in client side during  | 
 | 
            // renegotiation after a session-resumption abbreviated  | 
 | 
            // initial handshake?  | 
 | 
            //  | 
 | 
            // DO NOT need to check allowUnsafeServerCertChange here. We only  | 
 | 
            // reserve server certificates when allowUnsafeServerCertChange is  | 
 | 
              | 
 | 
            if (chc.reservedServerCerts != null &&  | 
 | 
                    !chc.handshakeSession.useExtendedMasterSecret) { | 
 | 
                // It is not necessary to check the certificate update if  | 
 | 
                  | 
 | 
                String identityAlg = chc.sslConfig.identificationProtocol;  | 
 | 
                if ((identityAlg == null || identityAlg.isEmpty()) &&  | 
 | 
                        !isIdentityEquivalent(x509Certs[0],  | 
 | 
                                chc.reservedServerCerts[0])) { | 
 | 
                    throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                            "server certificate change is restricted " +  | 
 | 
                            "during renegotiation");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (chc.staplingActive) { | 
 | 
                // Defer the certificate check until after we've received the  | 
 | 
                // CertificateStatus message.  If that message doesn't come in  | 
 | 
                // immediately following this message we will execute the  | 
 | 
                  | 
 | 
                chc.deferredCerts = x509Certs;  | 
 | 
            } else { | 
 | 
                  | 
 | 
                checkServerCerts(chc, x509Certs);  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            chc.handshakeCredentials.add(  | 
 | 
                new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));  | 
 | 
            chc.handshakeSession.setPeerCertificates(x509Certs);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        private static boolean isIdentityEquivalent(X509Certificate thisCert,  | 
 | 
                X509Certificate prevCert) { | 
 | 
            if (thisCert.equals(prevCert)) { | 
 | 
                return true;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            Collection<List<?>> thisSubjectAltNames = null;  | 
 | 
            try { | 
 | 
                thisSubjectAltNames = thisCert.getSubjectAlternativeNames();  | 
 | 
            } catch (CertificateParsingException cpe) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Attempt to obtain subjectAltNames extension failed!");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            Collection<List<?>> prevSubjectAltNames = null;  | 
 | 
            try { | 
 | 
                prevSubjectAltNames = prevCert.getSubjectAlternativeNames();  | 
 | 
            } catch (CertificateParsingException cpe) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Attempt to obtain subjectAltNames extension failed!");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (thisSubjectAltNames != null && prevSubjectAltNames != null) { | 
 | 
                // check the iPAddress field in subjectAltName extension  | 
 | 
                //  | 
 | 
                  | 
 | 
                Collection<String> thisSubAltIPAddrs =  | 
 | 
                            getSubjectAltNames(thisSubjectAltNames, 7);  | 
 | 
                Collection<String> prevSubAltIPAddrs =  | 
 | 
                            getSubjectAltNames(prevSubjectAltNames, 7);  | 
 | 
                if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null &&  | 
 | 
                    isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) { | 
 | 
                    return true;  | 
 | 
                }  | 
 | 
 | 
 | 
                // check the dNSName field in subjectAltName extension  | 
 | 
                  | 
 | 
                Collection<String> thisSubAltDnsNames =  | 
 | 
                            getSubjectAltNames(thisSubjectAltNames, 2);  | 
 | 
                Collection<String> prevSubAltDnsNames =  | 
 | 
                            getSubjectAltNames(prevSubjectAltNames, 2);  | 
 | 
                if (thisSubAltDnsNames != null && prevSubAltDnsNames != null &&  | 
 | 
                    isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) { | 
 | 
                    return true;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            X500Principal thisSubject = thisCert.getSubjectX500Principal();  | 
 | 
            X500Principal prevSubject = prevCert.getSubjectX500Principal();  | 
 | 
            X500Principal thisIssuer = thisCert.getIssuerX500Principal();  | 
 | 
            X500Principal prevIssuer = prevCert.getIssuerX500Principal();  | 
 | 
 | 
 | 
            return (!thisSubject.getName().isEmpty() &&  | 
 | 
                    !prevSubject.getName().isEmpty() &&  | 
 | 
                    thisSubject.equals(prevSubject) &&  | 
 | 
                    thisIssuer.equals(prevIssuer));  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        private static Collection<String> getSubjectAltNames(  | 
 | 
                Collection<List<?>> subjectAltNames, int type) { | 
 | 
            HashSet<String> subAltDnsNames = null;  | 
 | 
            for (List<?> subjectAltName : subjectAltNames) { | 
 | 
                int subjectAltNameType = (Integer)subjectAltName.get(0);  | 
 | 
                if (subjectAltNameType == type) { | 
 | 
                    String subAltDnsName = (String)subjectAltName.get(1);  | 
 | 
                    if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { | 
 | 
                        if (subAltDnsNames == null) { | 
 | 
                            subAltDnsNames =  | 
 | 
                                    new HashSet<>(subjectAltNames.size());  | 
 | 
                        }  | 
 | 
                        subAltDnsNames.add(subAltDnsName);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return subAltDnsNames;  | 
 | 
        }  | 
 | 
 | 
 | 
        private static boolean isEquivalent(Collection<String> thisSubAltNames,  | 
 | 
                Collection<String> prevSubAltNames) { | 
 | 
            for (String thisSubAltName : thisSubAltNames) { | 
 | 
                for (String prevSubAltName : prevSubAltNames) { | 
 | 
                    // Only allow the exactly match.  No wildcard character  | 
 | 
                      | 
 | 
                    if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { | 
 | 
                        return true;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return false;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        static void checkServerCerts(ClientHandshakeContext chc,  | 
 | 
                X509Certificate[] certs) throws IOException { | 
 | 
 | 
 | 
            X509TrustManager tm = chc.sslContext.getX509TrustManager();  | 
 | 
 | 
 | 
            // find out the key exchange algorithm used  | 
 | 
              | 
 | 
            String keyExchangeString;  | 
 | 
            if (chc.negotiatedCipherSuite.keyExchange ==  | 
 | 
                    CipherSuite.KeyExchange.K_RSA_EXPORT ||  | 
 | 
                    chc.negotiatedCipherSuite.keyExchange ==  | 
 | 
                            CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) { | 
 | 
                keyExchangeString = CipherSuite.KeyExchange.K_RSA.name;  | 
 | 
            } else { | 
 | 
                keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name;  | 
 | 
            }  | 
 | 
 | 
 | 
            try { | 
 | 
                if (tm instanceof X509ExtendedTrustManager) { | 
 | 
                    if (chc.conContext.transport instanceof SSLEngine) { | 
 | 
                        SSLEngine engine = (SSLEngine)chc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkServerTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            keyExchangeString,  | 
 | 
                            engine);  | 
 | 
                    } else { | 
 | 
                        SSLSocket socket = (SSLSocket)chc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkServerTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            keyExchangeString,  | 
 | 
                            socket);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // Unlikely to happen, because we have wrapped the old  | 
 | 
                      | 
 | 
                    throw new CertificateException(  | 
 | 
                            "Improper X509TrustManager implementation");  | 
 | 
                }  | 
 | 
 | 
 | 
                // Once the server certificate chain has been validated, set  | 
 | 
                  | 
 | 
                chc.handshakeSession.setPeerCertificates(certs);  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw chc.conContext.fatal(getCertificateAlert(chc, ce), ce);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private static void checkClientCerts(ServerHandshakeContext shc,  | 
 | 
                X509Certificate[] certs) throws IOException { | 
 | 
            X509TrustManager tm = shc.sslContext.getX509TrustManager();  | 
 | 
 | 
 | 
              | 
 | 
            PublicKey key = certs[0].getPublicKey();  | 
 | 
            String keyAlgorithm = key.getAlgorithm();  | 
 | 
            String authType;  | 
 | 
            switch (keyAlgorithm) { | 
 | 
                case "RSA":  | 
 | 
                case "DSA":  | 
 | 
                case "EC":  | 
 | 
                case "RSASSA-PSS":  | 
 | 
                    authType = keyAlgorithm;  | 
 | 
                    break;  | 
 | 
                default:  | 
 | 
                      | 
 | 
                    authType = "UNKNOWN";  | 
 | 
            }  | 
 | 
 | 
 | 
            try { | 
 | 
                if (tm instanceof X509ExtendedTrustManager) { | 
 | 
                    if (shc.conContext.transport instanceof SSLEngine) { | 
 | 
                        SSLEngine engine = (SSLEngine)shc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkClientTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            engine);  | 
 | 
                    } else { | 
 | 
                        SSLSocket socket = (SSLSocket)shc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkClientTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            socket);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // Unlikely to happen, because we have wrapped the old  | 
 | 
                      | 
 | 
                    throw new CertificateException(  | 
 | 
                            "Improper X509TrustManager implementation");  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        private static Alert getCertificateAlert(  | 
 | 
                ClientHandshakeContext chc, CertificateException cexc) { | 
 | 
            // The specific reason for the failure will determine how to  | 
 | 
              | 
 | 
            Alert alert = Alert.CERTIFICATE_UNKNOWN;  | 
 | 
 | 
 | 
            Throwable baseCause = cexc.getCause();  | 
 | 
            if (baseCause instanceof CertPathValidatorException) { | 
 | 
                CertPathValidatorException cpve =  | 
 | 
                        (CertPathValidatorException)baseCause;  | 
 | 
                Reason reason = cpve.getReason();  | 
 | 
                if (reason == BasicReason.REVOKED) { | 
 | 
                    alert = chc.staplingActive ?  | 
 | 
                            Alert.BAD_CERT_STATUS_RESPONSE :  | 
 | 
                            Alert.CERTIFICATE_REVOKED;  | 
 | 
                } else if (  | 
 | 
                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { | 
 | 
                    alert = chc.staplingActive ?  | 
 | 
                            Alert.BAD_CERT_STATUS_RESPONSE :  | 
 | 
                            Alert.CERTIFICATE_UNKNOWN;  | 
 | 
                } else if (reason == BasicReason.ALGORITHM_CONSTRAINED) { | 
 | 
                    alert = Alert.UNSUPPORTED_CERTIFICATE;  | 
 | 
                } else if (reason == BasicReason.EXPIRED) { | 
 | 
                    alert = Alert.CERTIFICATE_EXPIRED;  | 
 | 
                } else if (reason == BasicReason.INVALID_SIGNATURE ||  | 
 | 
                        reason == BasicReason.NOT_YET_VALID) { | 
 | 
                    alert = Alert.BAD_CERTIFICATE;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return alert;  | 
 | 
        }  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    static final class CertificateEntry { | 
 | 
        final byte[] encoded;         | 
 | 
        private final SSLExtensions extensions;  | 
 | 
 | 
 | 
        CertificateEntry(byte[] encoded, SSLExtensions extensions) { | 
 | 
            this.encoded = encoded;  | 
 | 
            this.extensions = extensions;  | 
 | 
        }  | 
 | 
 | 
 | 
        private int getEncodedSize() { | 
 | 
            int extLen = extensions.length();  | 
 | 
            if (extLen == 0) { | 
 | 
                extLen = 2;       | 
 | 
            }  | 
 | 
            return 3 + encoded.length + extLen;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                "\n'{'\n" + | 
 | 
                "{0}\n" +                        | 
 | 
                "  \"extensions\": '{'\n" + | 
 | 
                "{1}\n" + | 
 | 
                "  '}'\n" +  | 
 | 
                "'}',", Locale.ENGLISH);  | 
 | 
 | 
 | 
            Object x509Certs;  | 
 | 
            try { | 
 | 
                  | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                x509Certs =  | 
 | 
                    cf.generateCertificate(new ByteArrayInputStream(encoded));  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                  | 
 | 
                x509Certs = encoded;  | 
 | 
            }  | 
 | 
 | 
 | 
            Object[] messageFields = { | 
 | 
                SSLLogger.toString(x509Certs),  | 
 | 
                Utilities.indent(extensions.toString(), "    ")  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    static final class T13CertificateMessage extends HandshakeMessage { | 
 | 
        private final byte[] requestContext;  | 
 | 
        private final List<CertificateEntry> certEntries;  | 
 | 
 | 
 | 
        T13CertificateMessage(HandshakeContext context,  | 
 | 
                byte[] requestContext, X509Certificate[] certificates)  | 
 | 
                throws SSLException, CertificateException  { | 
 | 
            super(context);  | 
 | 
 | 
 | 
            this.requestContext = requestContext.clone();  | 
 | 
            this.certEntries = new LinkedList<>();  | 
 | 
            for (X509Certificate cert : certificates) { | 
 | 
                byte[] encoded = cert.getEncoded();  | 
 | 
                SSLExtensions extensions = new SSLExtensions(this);  | 
 | 
                certEntries.add(new CertificateEntry(encoded, extensions));  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        T13CertificateMessage(HandshakeContext handshakeContext,  | 
 | 
                byte[] requestContext, List<CertificateEntry> certificates) { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            this.requestContext = requestContext.clone();  | 
 | 
            this.certEntries = certificates;  | 
 | 
        }  | 
 | 
 | 
 | 
        T13CertificateMessage(HandshakeContext handshakeContext,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            // struct { | 
 | 
            //      opaque certificate_request_context<0..2^8-1>;  | 
 | 
            //      CertificateEntry certificate_list<0..2^24-1>;  | 
 | 
              | 
 | 
            if (m.remaining() < 4) { | 
 | 
                throw new SSLProtocolException(  | 
 | 
                        "Invalid Certificate message: " +  | 
 | 
                        "insufficient data (length=" + m.remaining() + ")");  | 
 | 
            }  | 
 | 
            this.requestContext = Record.getBytes8(m);  | 
 | 
 | 
 | 
            if (m.remaining() < 3) { | 
 | 
                throw new SSLProtocolException(  | 
 | 
                        "Invalid Certificate message: " +  | 
 | 
                        "insufficient certificate entries data (length=" +  | 
 | 
                        m.remaining() + ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            int listLen = Record.getInt24(m);  | 
 | 
            if (listLen != m.remaining()) { | 
 | 
                throw new SSLProtocolException(  | 
 | 
                    "Invalid Certificate message: " +  | 
 | 
                    "incorrect list length (length=" + listLen + ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLExtension[] enabledExtensions =  | 
 | 
                handshakeContext.sslConfig.getEnabledExtensions(  | 
 | 
                        SSLHandshake.CERTIFICATE);  | 
 | 
            List<CertificateEntry> certList = new LinkedList<>();  | 
 | 
            while (m.hasRemaining()) { | 
 | 
                  | 
 | 
                byte[] encodedCert = Record.getBytes24(m);  | 
 | 
                if (encodedCert.length == 0) { | 
 | 
                    throw new SSLProtocolException(  | 
 | 
                        "Invalid Certificate message: empty cert_data");  | 
 | 
                }  | 
 | 
 | 
 | 
                SSLExtensions extensions =  | 
 | 
                        new SSLExtensions(this, m, enabledExtensions);  | 
 | 
                certList.add(new CertificateEntry(encodedCert, extensions));  | 
 | 
                if (certList.size() > SSLConfiguration.maxCertificateChainLength) { | 
 | 
                    throw new SSLProtocolException(  | 
 | 
                            "The certificate chain length (" | 
 | 
                            + certList.size()  | 
 | 
                            + ") exceeds the maximum allowed length (" | 
 | 
                            + SSLConfiguration.maxCertificateChainLength  | 
 | 
                            + ")");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            this.certEntries = Collections.unmodifiableList(certList);  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public SSLHandshake handshakeType() { | 
 | 
            return SSLHandshake.CERTIFICATE;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public int messageLength() { | 
 | 
            int msgLen = 4 + requestContext.length;  | 
 | 
            for (CertificateEntry entry : certEntries) { | 
 | 
                msgLen += entry.getEncodedSize();  | 
 | 
            }  | 
 | 
 | 
 | 
            return msgLen;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void send(HandshakeOutStream hos) throws IOException { | 
 | 
            int entryListLen = 0;  | 
 | 
            for (CertificateEntry entry : certEntries) { | 
 | 
                entryListLen += entry.getEncodedSize();  | 
 | 
            }  | 
 | 
 | 
 | 
            hos.putBytes8(requestContext);  | 
 | 
            hos.putInt24(entryListLen);  | 
 | 
            for (CertificateEntry entry : certEntries) { | 
 | 
                hos.putBytes24(entry.encoded);  | 
 | 
                  | 
 | 
                if (entry.extensions.length() == 0) { | 
 | 
                    hos.putInt16(0);  | 
 | 
                } else { | 
 | 
                    entry.extensions.send(hos);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                "\"Certificate\": '{'\n" + | 
 | 
                "  \"certificate_request_context\": \"{0}\",\n" + | 
 | 
                "  \"certificate_list\": [{1}\n]\n" + | 
 | 
                "'}'",  | 
 | 
                Locale.ENGLISH);  | 
 | 
 | 
 | 
            StringBuilder builder = new StringBuilder(512);  | 
 | 
            for (CertificateEntry entry : certEntries) { | 
 | 
                builder.append(entry.toString());  | 
 | 
            }  | 
 | 
 | 
 | 
            Object[] messageFields = { | 
 | 
                Utilities.toHexString(requestContext),  | 
 | 
                Utilities.indent(builder.toString())  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class T13CertificateProducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private T13CertificateProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            HandshakeContext hc = (HandshakeContext)context;  | 
 | 
            if (hc.sslConfig.isClientMode) { | 
 | 
                return onProduceCertificate(  | 
 | 
                        (ClientHandshakeContext)context, message);  | 
 | 
            } else { | 
 | 
                return onProduceCertificate(  | 
 | 
                        (ServerHandshakeContext)context, message);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private byte[] onProduceCertificate(ServerHandshakeContext shc,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage)message;  | 
 | 
 | 
 | 
            SSLPossession pos = choosePossession(shc, clientHello);  | 
 | 
            if (pos == null) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "No available authentication scheme");  | 
 | 
            }  | 
 | 
 | 
 | 
            if (!(pos instanceof X509Possession)) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "No X.509 certificate for server authentication");  | 
 | 
            }  | 
 | 
 | 
 | 
            X509Possession x509Possession = (X509Possession)pos;  | 
 | 
            X509Certificate[] localCerts = x509Possession.popCerts;  | 
 | 
            if (localCerts == null || localCerts.length == 0) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "No X.509 certificate for server authentication");  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            shc.handshakePossessions.add(x509Possession);  | 
 | 
            shc.handshakeSession.setLocalPrivateKey(  | 
 | 
                    x509Possession.popPrivateKey);  | 
 | 
            shc.handshakeSession.setLocalCertificates(localCerts);  | 
 | 
            T13CertificateMessage cm;  | 
 | 
            try { | 
 | 
                cm = new T13CertificateMessage(shc, (new byte[0]), localCerts);  | 
 | 
            } catch (SSLException | CertificateException ce) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Failed to produce server Certificate message", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            // Check the OCSP stapling extensions and attempt  | 
 | 
            // to get responses.  If the resulting stapleParams is non  | 
 | 
              | 
 | 
            shc.stapleParams = StatusResponseManager.processStapling(shc);  | 
 | 
            shc.staplingActive = (shc.stapleParams != null);  | 
 | 
 | 
 | 
            // Process extensions for each CertificateEntry.  | 
 | 
            // Since there can be multiple CertificateEntries within a  | 
 | 
            // single CT message, we will pin a specific CertificateEntry  | 
 | 
            // into the ServerHandshakeContext so individual extension  | 
 | 
            // producers know which X509Certificate it is processing in  | 
 | 
              | 
 | 
            SSLExtension[] enabledCTExts = shc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.CERTIFICATE,  | 
 | 
                    Arrays.asList(ProtocolVersion.PROTOCOLS_OF_13));  | 
 | 
            for (CertificateEntry certEnt : cm.certEntries) { | 
 | 
                shc.currentCertEntry = certEnt;  | 
 | 
                certEnt.extensions.produce(shc, enabledCTExts);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine("Produced server Certificate message", cm); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            cm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        private static SSLPossession choosePossession(  | 
 | 
                HandshakeContext hc,  | 
 | 
                ClientHelloMessage clientHello) throws IOException { | 
 | 
            if (hc.peerRequestedCertSignSchemes == null ||  | 
 | 
                    hc.peerRequestedCertSignSchemes.isEmpty()) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.warning(  | 
 | 
                            "No signature_algorithms(_cert) in ClientHello");  | 
 | 
                }  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
 | 
 | 
            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;  | 
 | 
                }  | 
 | 
 | 
 | 
                // Don't select a signature scheme unless we will be able to  | 
 | 
                  | 
 | 
                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;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.warning("No available authentication scheme"); | 
 | 
            }  | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        private byte[] onProduceCertificate(ClientHandshakeContext chc,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage)message;  | 
 | 
            SSLPossession pos = choosePossession(chc, clientHello);  | 
 | 
            X509Certificate[] localCerts;  | 
 | 
            if (pos == null) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine("No available client authentication scheme"); | 
 | 
                }  | 
 | 
                localCerts = new X509Certificate[0];  | 
 | 
            } else { | 
 | 
                chc.handshakePossessions.add(pos);  | 
 | 
                if (!(pos instanceof X509Possession)) { | 
 | 
                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                        SSLLogger.fine(  | 
 | 
                            "No X.509 certificate for client authentication");  | 
 | 
                    }  | 
 | 
                    localCerts = new X509Certificate[0];  | 
 | 
                } else { | 
 | 
                    X509Possession x509Possession = (X509Possession)pos;  | 
 | 
                    localCerts = x509Possession.popCerts;  | 
 | 
                    chc.handshakeSession.setLocalPrivateKey(  | 
 | 
                            x509Possession.popPrivateKey);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (localCerts != null && localCerts.length != 0) { | 
 | 
                chc.handshakeSession.setLocalCertificates(localCerts);  | 
 | 
            } else { | 
 | 
                chc.handshakeSession.setLocalCertificates(null);  | 
 | 
            }  | 
 | 
 | 
 | 
            T13CertificateMessage cm;  | 
 | 
            try { | 
 | 
                cm = new T13CertificateMessage(  | 
 | 
                        chc, chc.certRequestContext, localCerts);  | 
 | 
            } catch (SSLException | CertificateException ce) { | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Failed to produce client Certificate message", ce);  | 
 | 
            }  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine("Produced client Certificate message", cm); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            cm.write(chc.handshakeOutput);  | 
 | 
            chc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class T13CertificateConsumer implements SSLConsumer { | 
 | 
          | 
 | 
        private T13CertificateConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer message) throws IOException { | 
 | 
              | 
 | 
            HandshakeContext hc = (HandshakeContext)context;  | 
 | 
 | 
 | 
              | 
 | 
            hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);  | 
 | 
            T13CertificateMessage cm = new T13CertificateMessage(hc, message);  | 
 | 
            if (hc.sslConfig.isClientMode) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Consuming server Certificate handshake message", cm);  | 
 | 
                }  | 
 | 
                onConsumeCertificate((ClientHandshakeContext)context, cm);  | 
 | 
            } else { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine(  | 
 | 
                        "Consuming client Certificate handshake message", cm);  | 
 | 
                }  | 
 | 
                onConsumeCertificate((ServerHandshakeContext)context, cm);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onConsumeCertificate(ServerHandshakeContext shc,  | 
 | 
                T13CertificateMessage certificateMessage )throws IOException { | 
 | 
            if (certificateMessage.certEntries == null ||  | 
 | 
                    certificateMessage.certEntries.isEmpty()) { | 
 | 
                // For empty Certificate messages, we should not expect  | 
 | 
                  | 
 | 
                shc.handshakeConsumers.remove(  | 
 | 
                        SSLHandshake.CERTIFICATE_VERIFY.id);  | 
 | 
                if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) { | 
 | 
                    throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                        "Empty client certificate chain");  | 
 | 
                } else { | 
 | 
                      | 
 | 
                    return;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            X509Certificate[] cliCerts =  | 
 | 
                    checkClientCerts(shc, certificateMessage.certEntries);  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            shc.handshakeCredentials.add(  | 
 | 
                new X509Credentials(cliCerts[0].getPublicKey(), cliCerts));  | 
 | 
            shc.handshakeSession.setPeerCertificates(cliCerts);  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onConsumeCertificate(ClientHandshakeContext chc,  | 
 | 
                T13CertificateMessage certificateMessage )throws IOException { | 
 | 
            if (certificateMessage.certEntries == null ||  | 
 | 
                    certificateMessage.certEntries.isEmpty()) { | 
 | 
                throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Empty server certificate chain");  | 
 | 
            }  | 
 | 
 | 
 | 
            // Each CertificateEntry will have its own set of extensions  | 
 | 
              | 
 | 
            SSLExtension[] enabledExtensions =  | 
 | 
                chc.sslConfig.getEnabledExtensions(SSLHandshake.CERTIFICATE);  | 
 | 
            for (CertificateEntry certEnt : certificateMessage.certEntries) { | 
 | 
                certEnt.extensions.consumeOnLoad(chc, enabledExtensions);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            X509Certificate[] srvCerts =  | 
 | 
                    checkServerCerts(chc, certificateMessage.certEntries);  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            chc.handshakeCredentials.add(  | 
 | 
                new X509Credentials(srvCerts[0].getPublicKey(), srvCerts));  | 
 | 
            chc.handshakeSession.setPeerCertificates(srvCerts);  | 
 | 
        }  | 
 | 
 | 
 | 
        private static X509Certificate[] checkClientCerts(  | 
 | 
                ServerHandshakeContext shc,  | 
 | 
                List<CertificateEntry> certEntries) throws IOException { | 
 | 
            X509Certificate[] certs =  | 
 | 
                    new X509Certificate[certEntries.size()];  | 
 | 
            try { | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                int i = 0;  | 
 | 
                for (CertificateEntry entry : certEntries) { | 
 | 
                    certs[i++] = (X509Certificate)cf.generateCertificate(  | 
 | 
                                    new ByteArrayInputStream(entry.encoded));  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Failed to parse server certificates", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            String keyAlgorithm = certs[0].getPublicKey().getAlgorithm();  | 
 | 
            String authType;  | 
 | 
            switch (keyAlgorithm) { | 
 | 
                case "RSA":  | 
 | 
                case "DSA":  | 
 | 
                case "EC":  | 
 | 
                case "RSASSA-PSS":  | 
 | 
                    authType = keyAlgorithm;  | 
 | 
                    break;  | 
 | 
                default:  | 
 | 
                      | 
 | 
                    authType = "UNKNOWN";  | 
 | 
            }  | 
 | 
 | 
 | 
            try { | 
 | 
                X509TrustManager tm = shc.sslContext.getX509TrustManager();  | 
 | 
                if (tm instanceof X509ExtendedTrustManager) { | 
 | 
                    if (shc.conContext.transport instanceof SSLEngine) { | 
 | 
                        SSLEngine engine = (SSLEngine)shc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkClientTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            engine);  | 
 | 
                    } else { | 
 | 
                        SSLSocket socket = (SSLSocket)shc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkClientTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            socket);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // Unlikely to happen, because we have wrapped the old  | 
 | 
                      | 
 | 
                    throw new CertificateException(  | 
 | 
                            "Improper X509TrustManager implementation");  | 
 | 
                }  | 
 | 
 | 
 | 
                // Once the client certificate chain has been validated, set  | 
 | 
                  | 
 | 
                shc.handshakeSession.setPeerCertificates(certs);  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            return certs;  | 
 | 
        }  | 
 | 
 | 
 | 
        private static X509Certificate[] checkServerCerts(  | 
 | 
                ClientHandshakeContext chc,  | 
 | 
                List<CertificateEntry> certEntries) throws IOException { | 
 | 
            X509Certificate[] certs =  | 
 | 
                    new X509Certificate[certEntries.size()];  | 
 | 
            try { | 
 | 
                CertificateFactory cf = CertificateFactory.getInstance("X.509"); | 
 | 
                int i = 0;  | 
 | 
                for (CertificateEntry entry : certEntries) { | 
 | 
                    certs[i++] = (X509Certificate)cf.generateCertificate(  | 
 | 
                                    new ByteArrayInputStream(entry.encoded));  | 
 | 
                }  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,  | 
 | 
                    "Failed to parse server certificates", ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            // find out the types of server authentication used  | 
 | 
            //  | 
 | 
            // Note that the "UNKNOWN" authentication type is sufficient to  | 
 | 
              | 
 | 
            String authType = "UNKNOWN";  | 
 | 
 | 
 | 
            try { | 
 | 
                X509TrustManager tm = chc.sslContext.getX509TrustManager();  | 
 | 
                if (tm instanceof X509ExtendedTrustManager) { | 
 | 
                    if (chc.conContext.transport instanceof SSLEngine) { | 
 | 
                        SSLEngine engine = (SSLEngine)chc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkServerTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            engine);  | 
 | 
                    } else { | 
 | 
                        SSLSocket socket = (SSLSocket)chc.conContext.transport;  | 
 | 
                        ((X509ExtendedTrustManager)tm).checkServerTrusted(  | 
 | 
                            certs.clone(),  | 
 | 
                            authType,  | 
 | 
                            socket);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // Unlikely to happen, because we have wrapped the old  | 
 | 
                      | 
 | 
                    throw new CertificateException(  | 
 | 
                            "Improper X509TrustManager implementation");  | 
 | 
                }  | 
 | 
 | 
 | 
                // Once the server certificate chain has been validated, set  | 
 | 
                  | 
 | 
                chc.handshakeSession.setPeerCertificates(certs);  | 
 | 
            } catch (CertificateException ce) { | 
 | 
                throw chc.conContext.fatal(getCertificateAlert(chc, ce), ce);  | 
 | 
            }  | 
 | 
 | 
 | 
            return certs;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        private static Alert getCertificateAlert(  | 
 | 
                ClientHandshakeContext chc, CertificateException cexc) { | 
 | 
            // The specific reason for the failure will determine how to  | 
 | 
              | 
 | 
            Alert alert = Alert.CERTIFICATE_UNKNOWN;  | 
 | 
 | 
 | 
            Throwable baseCause = cexc.getCause();  | 
 | 
            if (baseCause instanceof CertPathValidatorException) { | 
 | 
                CertPathValidatorException cpve =  | 
 | 
                        (CertPathValidatorException)baseCause;  | 
 | 
                Reason reason = cpve.getReason();  | 
 | 
                if (reason == BasicReason.REVOKED) { | 
 | 
                    alert = chc.staplingActive ?  | 
 | 
                            Alert.BAD_CERT_STATUS_RESPONSE :  | 
 | 
                            Alert.CERTIFICATE_REVOKED;  | 
 | 
                } else if (  | 
 | 
                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { | 
 | 
                    alert = chc.staplingActive ?  | 
 | 
                            Alert.BAD_CERT_STATUS_RESPONSE :  | 
 | 
                            Alert.CERTIFICATE_UNKNOWN;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return alert;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |