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