1ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra/* 2ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * Copyright (C) 2012 The Android Open Source Project 3ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 4ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 5ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * you may not use this file except in compliance with the License. 6ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * You may obtain a copy of the License at 7ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 8ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * http://www.apache.org/licenses/LICENSE-2.0 9ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 10ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * Unless required by applicable law or agreed to in writing, software 11ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 12ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * See the License for the specific language governing permissions and 14ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * limitations under the License. 15ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra */ 16ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 17ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condrapackage android.net.http; 18ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 19ec95c407b43a03890d3b42b86571110ee90d283dChad Brubakerimport android.annotation.SystemApi; 2000a4e299694ccf5edc07894ca46e38602e9b4a38Chad Brubakerimport android.security.net.config.UserCertificateSource; 21ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker 2212e752225aa96888358294be0d725d499a1c9f03Kenny Rootimport com.android.org.conscrypt.TrustManagerImpl; 238071124375e336b98de45b44b9884c92cdfd9bd8Andy Stadler 24bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubakerimport java.lang.reflect.InvocationTargetException; 25bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubakerimport java.lang.reflect.Method; 26ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condraimport java.security.cert.CertificateException; 27ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condraimport java.security.cert.X509Certificate; 28ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condraimport java.util.List; 29ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 30ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condraimport javax.net.ssl.X509TrustManager; 31ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 32ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra/** 33ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * X509TrustManager wrapper exposing Android-added features. 34da776c872e8880b48dfac4a9e9a2d90e398e4608Kenny Root * <p> 35da776c872e8880b48dfac4a9e9a2d90e398e4608Kenny Root * The checkServerTrusted method allows callers to perform additional 36da776c872e8880b48dfac4a9e9a2d90e398e4608Kenny Root * verification of certificate chains after they have been successfully verified 37da776c872e8880b48dfac4a9e9a2d90e398e4608Kenny Root * by the platform. 38da776c872e8880b48dfac4a9e9a2d90e398e4608Kenny Root * </p> 39ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra */ 40ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condrapublic class X509TrustManagerExtensions { 41ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 42bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker private final TrustManagerImpl mDelegate; 43bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used. 44bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker private final X509TrustManager mTrustManager; 45bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker private final Method mCheckServerTrusted; 465116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker private final Method mIsSameTrustConfiguration; 47ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 48ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra /** 49ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * Constructs a new X509TrustManagerExtensions wrapper. 50ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 51ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * @param tm A {@link X509TrustManager} as returned by TrustManagerFactory.getInstance(); 52ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * @throws IllegalArgumentException If tm is an unsupported TrustManager type. 53ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra */ 54ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException { 55cb4c5819758a7e2951bd4818383f7c5e10a5f016Geremy Condra if (tm instanceof TrustManagerImpl) { 56ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra mDelegate = (TrustManagerImpl) tm; 57bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker mTrustManager = null; 58bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker mCheckServerTrusted = null; 595116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker mIsSameTrustConfiguration = null; 60bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker return; 61bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 62bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker // Use duck typing if possible. 63bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker mDelegate = null; 64bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker mTrustManager = tm; 65bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker // Check that the hostname aware checkServerTrusted is present. 66bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker try { 67bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted", 68bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker X509Certificate[].class, 69bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker String.class, 70bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker String.class); 71bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } catch (NoSuchMethodException e) { 72bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker throw new IllegalArgumentException("Required method" 73bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker + " checkServerTrusted(X509Certificate[], String, String, String) missing"); 74bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 755116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker // Get the option isSameTrustConfiguration method. 765116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker Method isSameTrustConfiguration = null; 775116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker try { 785116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker isSameTrustConfiguration = tm.getClass().getMethod("isSameTrustConfiguration", 795116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker String.class, 805116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker String.class); 815116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } catch (ReflectiveOperationException ignored) { 825116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } 835116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker mIsSameTrustConfiguration = isSameTrustConfiguration; 84ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra } 85ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra 86ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra /** 87ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * Verifies the given certificate chain. 88ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 89ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * <p>See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a 90ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * description of the chain and authType parameters. The final parameter, host, should be the 91ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * hostname of the server.</p> 92ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * 93ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * @throws CertificateException if the chain does not verify correctly. 94ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra * @return the properly ordered chain used for verification as a list of X509Certificates. 95ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra */ 96ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, 97ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra String host) throws CertificateException { 98bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker if (mDelegate != null) { 99bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker return mDelegate.checkServerTrusted(chain, authType, host); 100bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } else { 101bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker try { 102bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain, 103bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker authType, host); 104bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } catch (IllegalAccessException e) { 105bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker throw new CertificateException("Failed to call checkServerTrusted", e); 106bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } catch (InvocationTargetException e) { 107bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker if (e.getCause() instanceof CertificateException) { 108bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker throw (CertificateException) e.getCause(); 109bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 110bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker if (e.getCause() instanceof RuntimeException) { 111bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker throw (RuntimeException) e.getCause(); 112bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 113bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker throw new CertificateException("checkServerTrusted failed", e.getCause()); 114bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 115bfcd67f71eedcefd269e63e72219ac6a3435626eChad Brubaker } 116ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra } 117d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh 118d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh /** 119d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh * Checks whether a CA certificate is added by an user. 120d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh * 12100a4e299694ccf5edc07894ca46e38602e9b4a38Chad Brubaker * <p>Since {@link X509TrustManager#checkServerTrusted} may allow its parameter {@code chain} to 122d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh * chain up to user-added CA certificates, this method can be used to perform additional 123d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh * policies for user-added CA certificates. 124d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh * 12500a4e299694ccf5edc07894ca46e38602e9b4a38Chad Brubaker * @return {@code true} to indicate that the certificate authority exists in the user added 12600a4e299694ccf5edc07894ca46e38602e9b4a38Chad Brubaker * certificate store, {@code false} otherwise. 127d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh */ 128d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh public boolean isUserAddedCertificate(X509Certificate cert) { 12900a4e299694ccf5edc07894ca46e38602e9b4a38Chad Brubaker return UserCertificateSource.getInstance().findBySubjectAndPublicKey(cert) != null; 130d96371521299627c8be59e0d9d5b42a61d0720beWilliam Luh } 131ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker 132ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker /** 133ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker * Returns {@code true} if the TrustManager uses the same trust configuration for the provided 134ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker * hostnames. 135ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker */ 136ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker @SystemApi 137ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker public boolean isSameTrustConfiguration(String hostname1, String hostname2) { 1385116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker if (mIsSameTrustConfiguration == null) { 1395116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker return true; 1405116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } 1415116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker try { 1425116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker return (Boolean) mIsSameTrustConfiguration.invoke(mTrustManager, hostname1, hostname2); 1435116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } catch (IllegalAccessException e) { 1445116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker throw new RuntimeException("Failed to call isSameTrustConfiguration", e); 1455116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } catch (InvocationTargetException e) { 1465116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker if (e.getCause() instanceof RuntimeException) { 1475116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker throw (RuntimeException) e.getCause(); 1485116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } else { 1495116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker throw new RuntimeException("isSameTrustConfiguration failed", e.getCause()); 1505116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } 1515116ac0d222e3c85f04c446c75d7211280f56ffaChad Brubaker } 152ec95c407b43a03890d3b42b86571110ee90d283dChad Brubaker } 153ed41a4e2d81fceafbddb1e9a4ca327535bb739efGeremy Condra} 154