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