NetworkSecurityTrustManager.java revision 0f5d386a769cafa3f4faf93f17acb22503b6fd3e
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 17package android.security.net.config; 18 19import com.android.org.conscrypt.TrustManagerImpl; 20 21import android.util.ArrayMap; 22import java.io.IOException; 23import java.net.Socket; 24import java.security.cert.CertificateException; 25import java.security.cert.X509Certificate; 26import java.security.GeneralSecurityException; 27import java.security.KeyStore; 28import java.security.MessageDigest; 29import java.util.List; 30import java.util.Map; 31import java.util.Set; 32 33import javax.net.ssl.SSLEngine; 34import 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 */ 41public 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 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 67 public void checkClientTrusted(X509Certificate[] chain, String authType) 68 throws CertificateException { 69 mDelegate.checkClientTrusted(chain, authType); 70 } 71 72 @Override 73 public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket) 74 throws CertificateException { 75 mDelegate.checkClientTrusted(certs, authType, socket); 76 } 77 78 @Override 79 public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine) 80 throws CertificateException { 81 mDelegate.checkClientTrusted(certs, authType, engine); 82 } 83 84 @Override 85 public void checkServerTrusted(X509Certificate[] certs, String authType) 86 throws CertificateException { 87 checkServerTrusted(certs, authType, (String) null); 88 } 89 90 @Override 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 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 */ 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 118 /** 119 * Check if the provided certificate is a user added certificate authority. 120 * This is required by android.net.http.X509TrustManagerExtensions. 121 */ 122 public boolean isUserAddedCertificate(X509Certificate cert) { 123 // TODO: Figure out the right way to handle this, and if it is still even used. 124 return false; 125 } 126 127 private void checkPins(List<X509Certificate> chain) throws CertificateException { 128 PinSet pinSet = mNetworkSecurityConfig.getPins(); 129 if (pinSet.pins.isEmpty() 130 || System.currentTimeMillis() > pinSet.expirationTime 131 || !isPinningEnforced(chain)) { 132 return; 133 } 134 Set<String> pinAlgorithms = pinSet.getPinAlgorithms(); 135 Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>( 136 pinAlgorithms.size()); 137 for (int i = chain.size() - 1; i >= 0 ; i--) { 138 X509Certificate cert = chain.get(i); 139 byte[] encodedSPKI = cert.getPublicKey().getEncoded(); 140 for (String algorithm : pinAlgorithms) { 141 MessageDigest md = digestMap.get(algorithm); 142 if (md == null) { 143 try { 144 md = MessageDigest.getInstance(algorithm); 145 } catch (GeneralSecurityException e) { 146 throw new RuntimeException(e); 147 } 148 digestMap.put(algorithm, md); 149 } 150 if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) { 151 return; 152 } 153 } 154 } 155 156 // TODO: Throw a subclass of CertificateException which indicates a pinning failure. 157 throw new CertificateException("Pin verification failed"); 158 } 159 160 private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException { 161 if (chain.isEmpty()) { 162 return false; 163 } 164 X509Certificate anchorCert = chain.get(chain.size() - 1); 165 TrustAnchor chainAnchor = 166 mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert); 167 if (chainAnchor == null) { 168 throw new CertificateException("Trusted chain does not end in a TrustAnchor"); 169 } 170 return !chainAnchor.overridesPins; 171 } 172 173 @Override 174 public X509Certificate[] getAcceptedIssuers() { 175 // TrustManagerImpl only looks at the provided KeyStore and not the TrustedCertificateStore 176 // for getAcceptedIssuers, so implement it here instead of delegating. 177 synchronized (mIssuersLock) { 178 if (mIssuers == null) { 179 Set<TrustAnchor> anchors = mNetworkSecurityConfig.getTrustAnchors(); 180 X509Certificate[] issuers = new X509Certificate[anchors.size()]; 181 int i = 0; 182 for (TrustAnchor anchor : anchors) { 183 issuers[i++] = anchor.certificate; 184 } 185 mIssuers = issuers; 186 } 187 return mIssuers.clone(); 188 } 189 } 190 191 public void handleTrustStorageUpdate() { 192 synchronized (mIssuersLock) { 193 mIssuers = null; 194 mDelegate.handleTrustStorageUpdate(); 195 } 196 } 197} 198