1 /* 2 * Copyright (C) 2015 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.security.net.config; 18 19 import com.android.org.conscrypt.TrustManagerImpl; 20 21 import android.util.ArrayMap; 22 import java.io.IOException; 23 import java.net.Socket; 24 import java.security.cert.CertificateException; 25 import java.security.cert.X509Certificate; 26 import java.security.GeneralSecurityException; 27 import java.security.KeyStore; 28 import java.security.MessageDigest; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Set; 32 33 import javax.net.ssl.SSLEngine; 34 import javax.net.ssl.X509ExtendedTrustManager; 35 36 /** 37 * {@link X509ExtendedTrustManager} that implements the trust anchor and pinning for a 38 * given {@link NetworkSecurityConfig}. 39 * @hide 40 */ 41 public class NetworkSecurityTrustManager extends X509ExtendedTrustManager { 42 // TODO: Replace this with a general X509TrustManager and use duck-typing. 43 private final TrustManagerImpl mDelegate; 44 private final NetworkSecurityConfig mNetworkSecurityConfig; 45 private final Object mIssuersLock = new Object(); 46 47 private X509Certificate[] mIssuers; 48 NetworkSecurityTrustManager(NetworkSecurityConfig config)49 public NetworkSecurityTrustManager(NetworkSecurityConfig config) { 50 if (config == null) { 51 throw new NullPointerException("config must not be null"); 52 } 53 mNetworkSecurityConfig = config; 54 try { 55 TrustedCertificateStoreAdapter certStore = new TrustedCertificateStoreAdapter(config); 56 // Provide an empty KeyStore since TrustManagerImpl doesn't support null KeyStores. 57 // TrustManagerImpl will use certStore to lookup certificates. 58 KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType()); 59 store.load(null); 60 mDelegate = new TrustManagerImpl(store, null, certStore); 61 } catch (GeneralSecurityException | IOException e) { 62 throw new RuntimeException(e); 63 } 64 } 65 66 @Override checkClientTrusted(X509Certificate[] chain, String authType)67 public void checkClientTrusted(X509Certificate[] chain, String authType) 68 throws CertificateException { 69 mDelegate.checkClientTrusted(chain, authType); 70 } 71 72 @Override checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)73 public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket) 74 throws CertificateException { 75 mDelegate.checkClientTrusted(certs, authType, socket); 76 } 77 78 @Override checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)79 public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine) 80 throws CertificateException { 81 mDelegate.checkClientTrusted(certs, authType, engine); 82 } 83 84 @Override checkServerTrusted(X509Certificate[] certs, String authType)85 public void checkServerTrusted(X509Certificate[] certs, String authType) 86 throws CertificateException { 87 checkServerTrusted(certs, authType, (String) null); 88 } 89 90 @Override checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)91 public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket) 92 throws CertificateException { 93 List<X509Certificate> trustedChain = 94 mDelegate.getTrustedChainForServer(certs, authType, socket); 95 checkPins(trustedChain); 96 } 97 98 @Override checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)99 public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine) 100 throws CertificateException { 101 List<X509Certificate> trustedChain = 102 mDelegate.getTrustedChainForServer(certs, authType, engine); 103 checkPins(trustedChain); 104 } 105 106 /** 107 * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}. 108 * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not 109 * modify without modifying those callers. 110 */ checkServerTrusted(X509Certificate[] certs, String authType, String host)111 public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType, 112 String host) throws CertificateException { 113 List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host); 114 checkPins(trustedChain); 115 return trustedChain; 116 } 117 checkPins(List<X509Certificate> chain)118 private void checkPins(List<X509Certificate> chain) throws CertificateException { 119 PinSet pinSet = mNetworkSecurityConfig.getPins(); 120 if (pinSet.pins.isEmpty() 121 || System.currentTimeMillis() > pinSet.expirationTime 122 || !isPinningEnforced(chain)) { 123 return; 124 } 125 Set<String> pinAlgorithms = pinSet.getPinAlgorithms(); 126 Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>( 127 pinAlgorithms.size()); 128 for (int i = chain.size() - 1; i >= 0 ; i--) { 129 X509Certificate cert = chain.get(i); 130 byte[] encodedSPKI = cert.getPublicKey().getEncoded(); 131 for (String algorithm : pinAlgorithms) { 132 MessageDigest md = digestMap.get(algorithm); 133 if (md == null) { 134 try { 135 md = MessageDigest.getInstance(algorithm); 136 } catch (GeneralSecurityException e) { 137 throw new RuntimeException(e); 138 } 139 digestMap.put(algorithm, md); 140 } 141 if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) { 142 return; 143 } 144 } 145 } 146 147 // TODO: Throw a subclass of CertificateException which indicates a pinning failure. 148 throw new CertificateException("Pin verification failed"); 149 } 150 isPinningEnforced(List<X509Certificate> chain)151 private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException { 152 if (chain.isEmpty()) { 153 return false; 154 } 155 X509Certificate anchorCert = chain.get(chain.size() - 1); 156 TrustAnchor chainAnchor = 157 mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert); 158 if (chainAnchor == null) { 159 throw new CertificateException("Trusted chain does not end in a TrustAnchor"); 160 } 161 return !chainAnchor.overridesPins; 162 } 163 164 @Override getAcceptedIssuers()165 public X509Certificate[] getAcceptedIssuers() { 166 // TrustManagerImpl only looks at the provided KeyStore and not the TrustedCertificateStore 167 // for getAcceptedIssuers, so implement it here instead of delegating. 168 synchronized (mIssuersLock) { 169 if (mIssuers == null) { 170 Set<TrustAnchor> anchors = mNetworkSecurityConfig.getTrustAnchors(); 171 X509Certificate[] issuers = new X509Certificate[anchors.size()]; 172 int i = 0; 173 for (TrustAnchor anchor : anchors) { 174 issuers[i++] = anchor.certificate; 175 } 176 mIssuers = issuers; 177 } 178 return mIssuers.clone(); 179 } 180 } 181 handleTrustStorageUpdate()182 public void handleTrustStorageUpdate() { 183 synchronized (mIssuersLock) { 184 mIssuers = null; 185 mDelegate.handleTrustStorageUpdate(); 186 } 187 } 188 } 189