1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.http; 18 19 import static com.android.org.conscrypt.flags.Flags.certificateTransparencyCheckservertrustedApi; 20 21 import android.annotation.FlaggedApi; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.security.Flags; 26 import android.security.net.config.UserCertificateSource; 27 28 import com.android.org.conscrypt.TrustManagerImpl; 29 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.security.cert.CertificateException; 33 import java.security.cert.X509Certificate; 34 import java.util.Collections; 35 import java.util.List; 36 37 import javax.net.ssl.X509TrustManager; 38 39 /** 40 * X509TrustManager wrapper exposing Android-added features. 41 * <p> 42 * The checkServerTrusted methods allow callers to provide some additional 43 * context for the verification. This is particularly useful when an SSLEngine 44 * or SSLSocket is not available. 45 * </p> 46 */ 47 public class X509TrustManagerExtensions { 48 49 private final TrustManagerImpl mDelegate; 50 // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used. 51 private final X509TrustManager mTrustManager; 52 private final Method mCheckServerTrusted; 53 private final Method mCheckServerTrustedOcspAndTlsData; 54 private final Method mIsSameTrustConfiguration; 55 56 /** 57 * Constructs a new X509TrustManagerExtensions wrapper. 58 * 59 * @param tm A {@link X509TrustManager} as returned by TrustManagerFactory.getInstance(); 60 * @throws IllegalArgumentException If tm is an unsupported TrustManager type. 61 */ X509TrustManagerExtensions(X509TrustManager tm)62 public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException { 63 if (tm instanceof TrustManagerImpl) { 64 mDelegate = (TrustManagerImpl) tm; 65 mTrustManager = null; 66 mCheckServerTrusted = null; 67 mCheckServerTrustedOcspAndTlsData = null; 68 mIsSameTrustConfiguration = null; 69 return; 70 } 71 // Use duck typing if possible. 72 mDelegate = null; 73 mTrustManager = tm; 74 // Check that the hostname aware checkServerTrusted is present. 75 try { 76 mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted", 77 X509Certificate[].class, 78 String.class, 79 String.class); 80 } catch (NoSuchMethodException e) { 81 throw new IllegalArgumentException("Required method" 82 + " checkServerTrusted(X509Certificate[], String, String) missing"); 83 } 84 // Check that the OCSP and TlsData aware checkServerTrusted is present. 85 Method checkServerTrustedOcspAndTlsData = null; 86 try { 87 checkServerTrustedOcspAndTlsData = tm.getClass().getMethod("checkServerTrusted", 88 X509Certificate[].class, 89 byte[].class, 90 byte[].class, 91 String.class, 92 String.class); 93 } catch (ReflectiveOperationException ignored) { } 94 mCheckServerTrustedOcspAndTlsData = checkServerTrustedOcspAndTlsData; 95 // Get the option isSameTrustConfiguration method. 96 Method isSameTrustConfiguration = null; 97 try { 98 isSameTrustConfiguration = tm.getClass().getMethod("isSameTrustConfiguration", 99 String.class, 100 String.class); 101 } catch (ReflectiveOperationException ignored) { 102 } 103 mIsSameTrustConfiguration = isSameTrustConfiguration; 104 } 105 106 /** 107 * Verifies the given certificate chain. 108 * 109 * <p>See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a 110 * description of the chain and authType parameters. The final parameter, host, should be the 111 * hostname of the server.</p> 112 * 113 * @throws CertificateException if the chain does not verify correctly. 114 * @return the properly ordered chain used for verification as a list of X509Certificates. 115 */ checkServerTrusted(X509Certificate[] chain, String authType, String host)116 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, 117 String host) throws CertificateException { 118 if (mDelegate != null) { 119 return mDelegate.checkServerTrusted(chain, authType, host); 120 } else { 121 try { 122 return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain, 123 authType, host); 124 } catch (IllegalAccessException e) { 125 throw new CertificateException("Failed to call checkServerTrusted", e); 126 } catch (InvocationTargetException e) { 127 if (e.getCause() instanceof CertificateException) { 128 throw (CertificateException) e.getCause(); 129 } 130 if (e.getCause() instanceof RuntimeException) { 131 throw (RuntimeException) e.getCause(); 132 } 133 throw new CertificateException("checkServerTrusted failed", e.getCause()); 134 } 135 } 136 } 137 138 /** 139 * Verifies the given certificate chain. 140 * 141 * <p>See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a 142 * description of the chain and authType parameters. The final parameter, host, should be the 143 * hostname of the server.</p> 144 * 145 * <p>ocspData and tlsSctData may be provided to verify any Signed Certificate Timestamp (SCT) 146 * attached to the connection. These are ASN.1 octet strings (SignedCertificateTimestampList) 147 * as described in RFC 6962, Section 3.3. Note that SCTs embedded in the certificate chain 148 * will automatically be processed. 149 * </p> 150 * 151 * @throws CertificateException if the chain does not verify correctly. 152 * @throws IllegalArgumentException if the TrustManager is not compatible. 153 * @return the properly ordered chain used for verification as a list of X509Certificates. 154 */ 155 @FlaggedApi(Flags.FLAG_CERTIFICATE_TRANSPARENCY_CONFIGURATION) 156 @NonNull checkServerTrusted( @uppressLint"ArrayReturn") @onNull X509Certificate[] chain, @Nullable byte[] ocspData, @Nullable byte[] tlsSctData, @NonNull String authType, @NonNull String host)157 public List<X509Certificate> checkServerTrusted( 158 @SuppressLint("ArrayReturn") @NonNull X509Certificate[] chain, 159 @Nullable byte[] ocspData, 160 @Nullable byte[] tlsSctData, 161 @NonNull String authType, 162 @NonNull String host) throws CertificateException { 163 List<X509Certificate> result; 164 if (mDelegate != null) { 165 if (certificateTransparencyCheckservertrustedApi()) { 166 result = mDelegate.checkServerTrusted(chain, ocspData, tlsSctData, authType, host); 167 return result == null ? Collections.emptyList() : result; 168 } else { 169 // The conscrypt mainline module does not have the required method. 170 throw new IllegalArgumentException("Required method" 171 + " checkServerTrusted(X509Certificate[], byte[], byte[], String, String)" 172 + " not available in TrustManagerImpl"); 173 } 174 } 175 if (mCheckServerTrustedOcspAndTlsData == null) { 176 throw new IllegalArgumentException("Required method" 177 + " checkServerTrusted(X509Certificate[], byte[], byte[], String, String)" 178 + " missing"); 179 } 180 try { 181 result = (List<X509Certificate>) mCheckServerTrustedOcspAndTlsData.invoke(mTrustManager, 182 chain, ocspData, tlsSctData, authType, host); 183 return result == null ? Collections.emptyList() : result; 184 } catch (IllegalAccessException e) { 185 throw new CertificateException("Failed to call checkServerTrusted", e); 186 } catch (InvocationTargetException e) { 187 if (e.getCause() instanceof CertificateException) { 188 throw (CertificateException) e.getCause(); 189 } 190 if (e.getCause() instanceof RuntimeException) { 191 throw (RuntimeException) e.getCause(); 192 } 193 throw new CertificateException("checkServerTrusted failed", e.getCause()); 194 } 195 } 196 197 /** 198 * Checks whether a CA certificate is added by an user. 199 * 200 * <p>Since {@link X509TrustManager#checkServerTrusted} may allow its parameter {@code chain} to 201 * chain up to user-added CA certificates, this method can be used to perform additional 202 * policies for user-added CA certificates. 203 * 204 * @return {@code true} to indicate that the certificate authority exists in the user added 205 * certificate store, {@code false} otherwise. 206 */ isUserAddedCertificate(X509Certificate cert)207 public boolean isUserAddedCertificate(X509Certificate cert) { 208 return UserCertificateSource.getInstance().findBySubjectAndPublicKey(cert) != null; 209 } 210 211 /** 212 * Returns {@code true} if the TrustManager uses the same trust configuration for the provided 213 * hostnames. 214 */ isSameTrustConfiguration(String hostname1, String hostname2)215 public boolean isSameTrustConfiguration(String hostname1, String hostname2) { 216 if (mIsSameTrustConfiguration == null) { 217 return true; 218 } 219 try { 220 return (Boolean) mIsSameTrustConfiguration.invoke(mTrustManager, hostname1, hostname2); 221 } catch (IllegalAccessException e) { 222 throw new RuntimeException("Failed to call isSameTrustConfiguration", e); 223 } catch (InvocationTargetException e) { 224 if (e.getCause() instanceof RuntimeException) { 225 throw (RuntimeException) e.getCause(); 226 } else { 227 throw new RuntimeException("isSameTrustConfiguration failed", e.getCause()); 228 } 229 } 230 } 231 } 232