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

Commit 85ffa26f authored by Huahui Wu's avatar Huahui Wu
Browse files

Expose the cert validator to JNI.

Change-Id: Ie31919e762b1f528b319c251ccfd891dd7738544
parent 3a3f24e3
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 {