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

Commit da776c87 authored by Kenny Root's avatar Kenny Root
Browse files

Use X509ExtendedTrustManager and not Conscrypt

This allows the new X509ExtendedTrustManager to be used instead of
Conscrypt directly.

Bug: 13103812
Change-Id: I736f475342395f26657f2371828a6d3a0065a50a
parent 88517170
Loading
Loading
Loading
Loading
+71 −29
Original line number Diff line number Diff line
@@ -16,22 +16,26 @@

package android.net.http;

import com.android.org.conscrypt.SSLParametersImpl;
import com.android.org.conscrypt.TrustManagerImpl;
import android.util.Slog;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.DefaultHostnameVerifier;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;

/**
 * Class responsible for all server certificate validation functionality
@@ -39,28 +43,51 @@ import javax.net.ssl.X509TrustManager;
 * {@hide}
 */
public class CertificateChainValidator {
    private static final String TAG = "CertificateChainValidator";

    private static class NoPreloadHolder {
        /**
     * The singleton instance of the certificate chain validator
         * The singleton instance of the certificate chain validator.
         */
    private static final CertificateChainValidator sInstance
            = new CertificateChainValidator();
        private static final CertificateChainValidator sInstance = new CertificateChainValidator();

        /**
         * The singleton instance of the hostname verifier.
         */
        private static final HostnameVerifier sVerifier = HttpsURLConnection
                .getDefaultHostnameVerifier();
    }

    private static final DefaultHostnameVerifier sVerifier
            = new DefaultHostnameVerifier();
    private X509ExtendedTrustManager mTrustManager;

    /**
     * @return The singleton instance of the certificates chain validator
     */
    public static CertificateChainValidator getInstance() {
        return sInstance;
        return NoPreloadHolder.sInstance;
    }

    /**
     * Creates a new certificate chain validator. This is a private constructor.
     * If you need a Certificate chain validator, call getInstance().
     */
    private CertificateChainValidator() {}
    private CertificateChainValidator() {
        try {
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
            for (TrustManager tm : tmf.getTrustManagers()) {
                if (tm instanceof X509ExtendedTrustManager) {
                    mTrustManager = (X509ExtendedTrustManager) tm;
                }
            }
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("X.509 TrustManager factory must be available", e);
        }

        if (mTrustManager == null) {
            throw new RuntimeException(
                    "None of the X.509 TrustManagers are X509ExtendedTrustManager");
        }
    }

    /**
     * Performs the handshake and server certificates validation
@@ -136,14 +163,27 @@ public class CertificateChainValidator {
     * Handles updates to credential storage.
     */
    public static void handleTrustStorageUpdate() {
        TrustManagerFactory tmf;
        try {
            tmf = TrustManagerFactory.getInstance("X.509");
        } catch (NoSuchAlgorithmException e) {
            Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory");
            return;
        }

        TrustManager[] tms = tmf.getTrustManagers();
        boolean sentUpdate = false;
        for (TrustManager tm : tms) {
            try {
            X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
            if( x509TrustManager instanceof TrustManagerImpl ) {
                TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
                trustManager.handleTrustStorageUpdate();
                Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate");
                updateMethod.setAccessible(true);
                updateMethod.invoke(tm);
                sentUpdate = true;
            } catch (Exception e) {
            }
        }
        } catch (KeyManagementException ignored) {
        if (!sentUpdate) {
            Slog.w(TAG, "Didn't find a TrustManager to handle CA list update");
        }
    }

@@ -166,7 +206,8 @@ public class CertificateChainValidator {

        boolean valid = domain != null
                && !domain.isEmpty()
                && sVerifier.verify(domain, currCertificate);
                && NoPreloadHolder.sVerifier.verify(domain,
                        new DelegatingSSLSession.CertificateWrap(currCertificate));
        if (!valid) {
            if (HttpLog.LOGV) {
                HttpLog.v("certificate not for this host: " + domain);
@@ -175,13 +216,8 @@ public class CertificateChainValidator {
        }

        try {
            X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
            if (x509TrustManager instanceof TrustManagerImpl) {
                TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
                trustManager.checkServerTrusted(chain, authType, domain);
            } else {
                x509TrustManager.checkServerTrusted(chain, authType);
            }
            getInstance().getTrustManager().checkServerTrusted(chain, authType,
                    new DelegatingSocketWrapper(domain));
            return null;  // No errors.
        } catch (GeneralSecurityException e) {
            if (HttpLog.LOGV) {
@@ -192,6 +228,12 @@ public class CertificateChainValidator {
        }
    }

    /**
     * Returns the platform default {@link X509ExtendedTrustManager}.
     */
    private X509ExtendedTrustManager getTrustManager() {
        return mTrustManager;
    }

    private void closeSocketThrowException(
            SSLSocket socket, String errorMessage, String defaultErrorMessage)
+172 −0
Original line number Diff line number Diff line
/*
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.http;

import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;

/**
 * This is used when only a {@code hostname} is available but usage of the new API
 * {@link X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, Socket)}
 * requires a {@link SSLSocket}.
 *
 * @hide
 */
public class DelegatingSSLSession implements SSLSession {
    protected DelegatingSSLSession() {
    }

    public static class HostnameWrap extends DelegatingSSLSession {
        private final String mHostname;

        public HostnameWrap(String hostname) {
            mHostname = hostname;
        }

        @Override
        public String getPeerHost() {
            return mHostname;
        }
    }

    public static class CertificateWrap extends DelegatingSSLSession {
        private final Certificate mCertificate;

        public CertificateWrap(Certificate certificate) {
            mCertificate = certificate;
        }

        @Override
        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
            return new Certificate[] { mCertificate };
        }
    }


    @Override
    public int getApplicationBufferSize() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getCipherSuite() {
        throw new UnsupportedOperationException();
    }

    @Override
    public long getCreationTime() {
        throw new UnsupportedOperationException();
    }

    @Override
    public byte[] getId() {
        throw new UnsupportedOperationException();
    }

    @Override
    public long getLastAccessedTime() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Certificate[] getLocalCertificates() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Principal getLocalPrincipal() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getPacketBufferSize() {
        throw new UnsupportedOperationException();
    }

    @Override
    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
            throws SSLPeerUnverifiedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getPeerHost() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getPeerPort() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getProtocol() {
        throw new UnsupportedOperationException();
    }

    @Override
    public SSLSessionContext getSessionContext() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object getValue(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String[] getValueNames() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void invalidate() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isValid() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void putValue(String name, Object value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void removeValue(String name) {
        throw new UnsupportedOperationException();
    }
}
 No newline at end of file
+127 −0
Original line number Diff line number Diff line
/*
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.http;

import java.io.IOException;

import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;

/**
 * This is used when only a {@code hostname} is available for
 * {@link X509ExtendedTrustManager#checkServerTrusted(java.security.cert.X509Certificate[], String, Socket)}
 * but we want to use the new API that requires a {@link SSLSocket}.
 */
class DelegatingSocketWrapper extends SSLSocket {
    private String hostname;

    public DelegatingSocketWrapper(String hostname) {
        this.hostname = hostname;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String[] getSupportedProtocols() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String[] getEnabledProtocols() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        throw new UnsupportedOperationException();
    }

    @Override
    public SSLSession getSession() {
        return new DelegatingSSLSession.HostnameWrap(hostname);
    }

    @Override
    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void startHandshake() throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setUseClientMode(boolean mode) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean getUseClientMode() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setWantClientAuth(boolean want) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean getNeedClientAuth() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean getWantClientAuth() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean getEnableSessionCreation() {
        throw new UnsupportedOperationException();
    }
}
 No newline at end of file
+17 −5
Original line number Diff line number Diff line
@@ -22,14 +22,25 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * X509TrustManager wrapper exposing Android-added features.
 *
 * <p> The checkServerTrusted method allows callers to perform additional
 * verification of certificate chains after they have been successfully
 * verified by the platform.</p>
 * <p>
 * The checkServerTrusted method allows callers to perform additional
 * verification of certificate chains after they have been successfully verified
 * by the platform.
 * </p>
 * <p>
 * If the returned certificate list is not needed, see also
 * {@code X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, java.net.Socket)}
 * where an {@link SSLSocket} can be used to verify the given hostname during
 * handshake using
 * {@code SSLParameters#setEndpointIdentificationAlgorithm(String)}.
 * </p>
 */
public class X509TrustManagerExtensions {

@@ -61,6 +72,7 @@ public class X509TrustManagerExtensions {
     */
    public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
                                                    String host) throws CertificateException {
        return mDelegate.checkServerTrusted(chain, authType, host);
        return mDelegate.checkServerTrusted(chain, authType,
                new DelegatingSSLSession.HostnameWrap(host));
    }
}