Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5912f90e authored by Huahui Wu's avatar Huahui Wu Committed by Android (Google) Code Review
Browse files

Merge "Expose the cert validator to JNI."

parents d1604c73 85ffa26f
Loading
Loading
Loading
Loading
+54 −43
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.net.http;

import com.android.internal.net.DomainNameValidator;

import org.apache.harmony.security.provider.cert.X509CertImpl;
import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl;

import java.io.IOException;
@@ -78,10 +79,8 @@ class CertificateChainValidator {
    public SslError doHandshakeAndValidateServerCertificates(
            HttpsConnection connection, SSLSocket sslSocket, String domain)
            throws IOException {
        X509Certificate[] serverCertificates = null;

        // get a valid SSLSession, close the socket if we fail
        SSLSession sslSession = sslSession = sslSocket.getSession();
        SSLSession sslSession = sslSocket.getSession();
        if (!sslSession.isValid()) {
            closeSocketThrowException(sslSocket, "failed to perform SSL handshake");
        }
@@ -90,70 +89,82 @@ class CertificateChainValidator {
        Certificate[] peerCertificates =
            sslSocket.getSession().getPeerCertificates();

        if (peerCertificates == null || peerCertificates.length <= 0) {
        if (peerCertificates == null || peerCertificates.length == 0) {
            closeSocketThrowException(
                sslSocket, "failed to retrieve peer certificates");
        } else {
            serverCertificates =
                new X509Certificate[peerCertificates.length];
            for (int i = 0; i < peerCertificates.length; ++i) {
                serverCertificates[i] =
                    (X509Certificate)(peerCertificates[i]);
            }

            // update the SSL certificate associated with the connection
            if (connection != null) {
                if (serverCertificates[0] != null) {
                if (peerCertificates[0] != null) {
                    connection.setCertificate(
                        new SslCertificate(serverCertificates[0]));
                        new SslCertificate((X509Certificate)peerCertificates[0]));
                }
            }
        }

        return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain);
    }

    /**
     * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use
     * by Chromium HTTPS stack to validate the cert chain.
     * @param certChain The bytes for certificates in ASN.1 DER encoded certficates format.
     * @param domain The full website hostname and domain
     * @return An SSL error object if there is an error and null otherwise
     */
    public static SslError verifyServerCertificates(
        byte[][] certChain, String domain, String authType)
        throws IOException {

        if (certChain == null || certChain.length == 0) {
            throw new IllegalArgumentException("bad certificate chain");
        }

        X509Certificate[] serverCertificates = new X509Certificate[certChain.length];

        for (int i = 0; i < certChain.length; ++i) {
            serverCertificates[i] = new X509CertImpl(certChain[i]);
        }

        return verifyServerDomainAndCertificates(serverCertificates, domain);
    }

    /**
     * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates.
     * Calls DomainNamevalidator to valide the domain, and TrustManager to valide the certs.
     * @param chain the cert chain in X509 cert format.
     * @param domain The full website hostname and domain
     * @return An SSL error object if there is an error and null otherwise
     */
    private static SslError verifyServerDomainAndCertificates(
            X509Certificate[] chain, String domain)
            throws IOException {
        // check if the first certificate in the chain is for this site
        X509Certificate currCertificate = serverCertificates[0];
        X509Certificate currCertificate = chain[0];
        if (currCertificate == null) {
            closeSocketThrowException(
                sslSocket, "certificate for this site is null");
        } else {
            if (!DomainNameValidator.match(currCertificate, domain)) {
                String errorMessage = "certificate not for this host: " + domain;

                if (HttpLog.LOGV) {
                    HttpLog.v(errorMessage);
            throw new IllegalArgumentException("certificate for this site is null");
        }

                sslSocket.getSession().invalidate();
                return new SslError(
                    SslError.SSL_IDMISMATCH, currCertificate);
        if (!DomainNameValidator.match(currCertificate, domain)) {
            if (HttpLog.LOGV) {
                HttpLog.v("certificate not for this host: " + domain);
            }
            return new SslError(SslError.SSL_IDMISMATCH, currCertificate);
        }

        // first, we validate the new chain using the standard validation
        // solution; if we do not find any errors, we are done; if we
        // fail the standard validation, we re-validate again below,
        // this time trying to retrieve any individual errors we can
        // report back to the user.
        //
        try {
            SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(
                serverCertificates, "RSA");

            // no errors!!!
            return null;
            SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(chain, "RSA");
            return null;  // No errors.
        } catch (CertificateException e) {
            sslSocket.getSession().invalidate();

            if (HttpLog.LOGV) {
                HttpLog.v(
                    "failed to pre-validate the certificate chain, error: " +
                HttpLog.v("failed to validate the certificate chain, error: " +
                    e.getMessage());
            }
            return new SslError(
                SslError.SSL_UNTRUSTED, currCertificate);
            return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
        }
    }


    private void closeSocketThrowException(
            SSLSocket socket, String errorMessage, String defaultErrorMessage)
            throws IOException {