NetworkSecurityTrustManager.java revision 6bc1e3966c4890ee3d47b5e527b800f2700ed627
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.security.cert.CertificateException; 24import java.security.cert.X509Certificate; 25import java.security.GeneralSecurityException; 26import java.security.KeyStore; 27import java.security.MessageDigest; 28import java.util.List; 29import java.util.Map; 30import java.util.Set; 31 32import javax.net.ssl.X509TrustManager; 33 34/** 35 * {@link X509TrustManager} that implements the trust anchor and pinning for a 36 * given {@link NetworkSecurityConfig}. 37 * @hide 38 */ 39public class NetworkSecurityTrustManager implements X509TrustManager { 40 // TODO: Replace this with a general X509TrustManager and use duck-typing. 41 private final TrustManagerImpl mDelegate; 42 private final NetworkSecurityConfig mNetworkSecurityConfig; 43 44 public NetworkSecurityTrustManager(NetworkSecurityConfig config) { 45 if (config == null) { 46 throw new NullPointerException("config must not be null"); 47 } 48 mNetworkSecurityConfig = config; 49 // TODO: Create our own better KeyStoreImpl 50 try { 51 KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType()); 52 store.load(null); 53 int certNum = 0; 54 for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) { 55 store.setEntry(String.valueOf(certNum++), 56 new KeyStore.TrustedCertificateEntry(anchor.certificate), 57 null); 58 } 59 mDelegate = new TrustManagerImpl(store); 60 } catch (GeneralSecurityException | IOException e) { 61 throw new RuntimeException(e); 62 } 63 } 64 65 @Override 66 public void checkClientTrusted(X509Certificate[] chain, String authType) 67 throws CertificateException { 68 throw new CertificateException("Client authentication not supported"); 69 } 70 71 @Override 72 public void checkServerTrusted(X509Certificate[] certs, String authType) 73 throws CertificateException { 74 List<X509Certificate> trustedChain = 75 mDelegate.checkServerTrusted(certs, authType, (String) null); 76 checkPins(trustedChain); 77 } 78 79 private void checkPins(List<X509Certificate> chain) throws CertificateException { 80 PinSet pinSet = mNetworkSecurityConfig.getPins(); 81 if (pinSet.pins.isEmpty() 82 || System.currentTimeMillis() > pinSet.expirationTime 83 || !isPinningEnforced(chain)) { 84 return; 85 } 86 Set<String> pinAlgorithms = pinSet.getPinAlgorithms(); 87 Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>( 88 pinAlgorithms.size()); 89 for (int i = chain.size() - 1; i >= 0 ; i--) { 90 X509Certificate cert = chain.get(i); 91 byte[] encodedSPKI = cert.getPublicKey().getEncoded(); 92 for (String algorithm : pinAlgorithms) { 93 MessageDigest md = digestMap.get(algorithm); 94 if (md == null) { 95 try { 96 md = MessageDigest.getInstance(algorithm); 97 } catch (GeneralSecurityException e) { 98 throw new RuntimeException(e); 99 } 100 digestMap.put(algorithm, md); 101 } 102 if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) { 103 return; 104 } 105 } 106 } 107 108 // TODO: Throw a subclass of CertificateException which indicates a pinning failure. 109 throw new CertificateException("Pin verification failed"); 110 } 111 112 private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException { 113 if (chain.isEmpty()) { 114 return false; 115 } 116 X509Certificate anchorCert = chain.get(chain.size() - 1); 117 TrustAnchor chainAnchor = null; 118 // TODO: faster lookup 119 for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) { 120 if (anchor.certificate.equals(anchorCert)) { 121 chainAnchor = anchor; 122 break; 123 } 124 } 125 if (chainAnchor == null) { 126 throw new CertificateException("Trusted chain does not end in a TrustAnchor"); 127 } 128 return !chainAnchor.overridesPins; 129 } 130 131 @Override 132 public X509Certificate[] getAcceptedIssuers() { 133 return new X509Certificate[0]; 134 } 135} 136