diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java index eb4cedab83b79c5cec1d457b31da3b25c0f7a3a6..7c23c2319f4d5edbae17ad2fb5619ba38d3ce3c9 100644 --- a/core/java/android/net/http/X509TrustManagerExtensions.java +++ b/core/java/android/net/http/X509TrustManagerExtensions.java @@ -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 { - mDelegate = null; - throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() + - " which is not a supported type of X509TrustManager"); + mTrustManager = null; + mCheckServerTrusted = null; + mIsUserAddedCertificate = null; + return; + } + // Use duck typing if possible. + mDelegate = null; + 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 checkServerTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException { - return mDelegate.checkServerTrusted(chain, authType, host); + if (mDelegate != null) { + return mDelegate.checkServerTrusted(chain, authType, host); + } else { + try { + return (List) 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) { - return mDelegate.isUserAddedCertificate(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()); + } + } + } } }