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

Commit c1d863e7 authored by Kenny Root's avatar Kenny Root Committed by Android Git Automerger
Browse files

am 77ceb5e8: Merge "Use X509ExtendedTrustManager and not Conscrypt"

* commit '77ceb5e8':
  Use X509ExtendedTrustManager and not Conscrypt
parents 9bbd2f97 77ceb5e8
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));
    }
}