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

Commit bfcd67f7 authored by Chad Brubaker's avatar Chad Brubaker
Browse files

Use duck typing in X509TrustManagerExtensions

X509TrustManagerExtensions assumes that the default X509TrustManager is
an instance of conscrypt's TrustManagerImpl. That's no longer going to
always be the case. Instead use duck typing to support any
X509TrustManagers that have the extra methods required for
X509TrustManagerExtensions.

Change-Id: If23471bda590d5e131bb1e802a60599957bc7f37
parent d1c469e8
Loading
Loading
Loading
Loading
+66 −7
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package android.net.http;

import com.android.org.conscrypt.TrustManagerImpl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
@@ -34,7 +37,11 @@ import javax.net.ssl.X509TrustManager;
 */
public class X509TrustManagerExtensions {

    final TrustManagerImpl mDelegate;
    private final TrustManagerImpl mDelegate;
    // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used.
    private final X509TrustManager mTrustManager;
    private final Method mCheckServerTrusted;
    private final Method mIsUserAddedCertificate;

    /**
     * Constructs a new X509TrustManagerExtensions wrapper.
@@ -45,10 +52,31 @@ public class X509TrustManagerExtensions {
    public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException {
        if (tm instanceof TrustManagerImpl) {
            mDelegate = (TrustManagerImpl) tm;
        } else {
            mTrustManager = null;
            mCheckServerTrusted = null;
            mIsUserAddedCertificate = null;
            return;
        }
        // Use duck typing if possible.
        mDelegate = null;
            throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
                    " which is not a supported type of X509TrustManager");
        mTrustManager = tm;
        // Check that the hostname aware checkServerTrusted is present.
        try {
            mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted",
                    X509Certificate[].class,
                    String.class,
                    String.class);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Required method"
                    + " checkServerTrusted(X509Certificate[], String, String, String) missing");
        }
        // Check that isUserAddedCertificate is present.
        try {
            mIsUserAddedCertificate = tm.getClass().getMethod("isUserAddedCertificate",
                    X509Certificate.class);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(
                    "Required method isUserAddedCertificate(X509Certificate) missing");
        }
    }

@@ -64,7 +92,24 @@ public class X509TrustManagerExtensions {
     */
    public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
                                                    String host) throws CertificateException {
        if (mDelegate != null) {
            return mDelegate.checkServerTrusted(chain, authType, host);
        } else {
            try {
                return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain,
                        authType, host);
            } catch (IllegalAccessException e) {
                throw new CertificateException("Failed to call checkServerTrusted", e);
            } catch (InvocationTargetException e) {
                if (e.getCause() instanceof CertificateException) {
                    throw (CertificateException) e.getCause();
                }
                if (e.getCause() instanceof RuntimeException) {
                    throw (RuntimeException) e.getCause();
                }
                throw new CertificateException("checkServerTrusted failed", e.getCause());
            }
        }
    }

    /**
@@ -78,6 +123,20 @@ public class X509TrustManagerExtensions {
     * otherwise.
     */
    public boolean isUserAddedCertificate(X509Certificate cert) {
        if (mDelegate != null) {
            return mDelegate.isUserAddedCertificate(cert);
        } else {
            try {
                return (Boolean) mIsUserAddedCertificate.invoke(mTrustManager, cert);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to call isUserAddedCertificate", e);
            } catch (InvocationTargetException e) {
                if (e.getCause() instanceof RuntimeException) {
                    throw (RuntimeException) e.getCause();
                } else {
                    throw new RuntimeException("isUserAddedCertificate failed", e.getCause());
                }
            }
        }
    }
}