1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.xnet.provider.jsse; 19 20 import java.security.InvalidAlgorithmParameterException; 21 import java.security.KeyStore; 22 import java.security.KeyStoreException; 23 import java.security.cert.CertPath; 24 import java.security.cert.CertPathValidator; 25 import java.security.cert.CertPathValidatorException; 26 import java.security.cert.CertificateException; 27 import java.security.cert.CertificateFactory; 28 import java.security.cert.PKIXParameters; 29 import java.security.cert.TrustAnchor; 30 import java.security.cert.X509Certificate; 31 import java.util.Arrays; 32 import java.util.ArrayList; 33 import java.util.Enumeration; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Set; 37 import javax.net.ssl.X509TrustManager; 38 39 /** 40 * 41 * TrustManager implementation. The implementation is based on CertPathValidator 42 * PKIX and CertificateFactory X509 implementations. This implementations should 43 * be provided by some certification provider. 44 * 45 * @see javax.net.ssl.X509TrustManager 46 */ 47 public final class TrustManagerImpl implements X509TrustManager { 48 49 /** 50 * The AndroidCAStore if non-null, null otherwise. 51 */ 52 private final KeyStore rootKeyStore; 53 54 /** 55 * The backing store for the AndroidCAStore if non-null. This will 56 * be null when the rootKeyStore is null, implying we are not 57 * using the AndroidCAStore. 58 */ 59 private final TrustedCertificateStore trustedCertificateStore; 60 61 private final CertPathValidator validator; 62 63 /** 64 * An index of TrustAnchor instances that we've seen. Unlike the 65 * TrustedCertificateStore, this may contain intermediate CAs. 66 */ 67 private final TrustedCertificateIndex trustedCertificateIndex; 68 69 /** 70 * This is lazily initialized in the AndroidCAStore case since it 71 * forces us to bring all the CAs into memory. In the 72 * non-AndroidCAStore, we initialize this as part of the 73 * constructor. 74 */ 75 private final X509Certificate[] acceptedIssuers; 76 77 private final Exception err; 78 private final CertificateFactory factory; 79 80 /** 81 * Creates X509TrustManager based on a keystore 82 * 83 * @param ks 84 */ 85 public TrustManagerImpl(KeyStore keyStore) { 86 CertPathValidator validatorLocal = null; 87 CertificateFactory factoryLocal = null; 88 KeyStore rootKeyStoreLocal = null; 89 TrustedCertificateStore trustedCertificateStoreLocal = null; 90 TrustedCertificateIndex trustedCertificateIndexLocal = null; 91 X509Certificate[] acceptedIssuersLocal = null; 92 Exception errLocal = null; 93 try { 94 validatorLocal = CertPathValidator.getInstance("PKIX"); 95 factoryLocal = CertificateFactory.getInstance("X509"); 96 97 // if we have an AndroidCAStore, we will lazily load CAs 98 if ("AndroidCAStore".equals(keyStore.getType())) { 99 rootKeyStoreLocal = keyStore; 100 trustedCertificateStoreLocal = new TrustedCertificateStore(); 101 acceptedIssuersLocal = null; 102 trustedCertificateIndexLocal = new TrustedCertificateIndex(); 103 } else { 104 rootKeyStoreLocal = null; 105 trustedCertificateStoreLocal = null; 106 acceptedIssuersLocal = acceptedIssuers(keyStore); 107 trustedCertificateIndexLocal 108 = new TrustedCertificateIndex(trustAnchors(acceptedIssuersLocal)); 109 } 110 111 } catch (Exception e) { 112 errLocal = e; 113 } 114 this.rootKeyStore = rootKeyStoreLocal; 115 this.trustedCertificateStore = trustedCertificateStoreLocal; 116 this.validator = validatorLocal; 117 this.factory = factoryLocal; 118 this.trustedCertificateIndex = trustedCertificateIndexLocal; 119 this.acceptedIssuers = acceptedIssuersLocal; 120 this.err = errLocal; 121 } 122 123 private static X509Certificate[] acceptedIssuers(KeyStore ks) { 124 try { 125 // Note that unlike the PKIXParameters code to create a Set of 126 // TrustAnchors from a KeyStore, this version takes from both 127 // TrustedCertificateEntry and PrivateKeyEntry, not just 128 // TrustedCertificateEntry, which is why TrustManagerImpl 129 // cannot just use an PKIXParameters(KeyStore) 130 // constructor. 131 132 // TODO remove duplicates if same cert is found in both a 133 // PrivateKeyEntry and TrustedCertificateEntry 134 List<X509Certificate> trusted = new ArrayList<X509Certificate>(); 135 for (Enumeration<String> en = ks.aliases(); en.hasMoreElements();) { 136 final String alias = en.nextElement(); 137 final X509Certificate cert = (X509Certificate) ks.getCertificate(alias); 138 if (cert != null) { 139 trusted.add(cert); 140 } 141 } 142 return trusted.toArray(new X509Certificate[trusted.size()]); 143 } catch (KeyStoreException e) { 144 return new X509Certificate[0]; 145 } 146 } 147 148 private static Set<TrustAnchor> trustAnchors(X509Certificate[] certs) { 149 Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(certs.length); 150 for (X509Certificate cert : certs) { 151 trustAnchors.add(new TrustAnchor(cert, null)); 152 } 153 return trustAnchors; 154 } 155 156 @Override public void checkClientTrusted(X509Certificate[] chain, String authType) 157 throws CertificateException { 158 checkTrusted(chain, authType); 159 } 160 161 @Override public void checkServerTrusted(X509Certificate[] chain, String authType) 162 throws CertificateException { 163 checkTrusted(chain, authType); 164 } 165 166 private void checkTrusted(X509Certificate[] chain, String authType) 167 throws CertificateException { 168 if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) { 169 throw new IllegalArgumentException("null or zero-length parameter"); 170 } 171 if (err != null) { 172 throw new CertificateException(err); 173 } 174 175 Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(); 176 X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchors); 177 if (newChain.length == 0) { 178 // chain was entirely trusted, skip the validator 179 return; 180 } 181 182 CertPath certPath = factory.generateCertPath(Arrays.asList(newChain)); 183 if (trustAnchors.isEmpty()) { 184 throw new CertificateException(new CertPathValidatorException( 185 "Trust anchor for certification path not found.", null, certPath, -1)); 186 } 187 188 try { 189 PKIXParameters params = new PKIXParameters(trustAnchors); 190 params.setRevocationEnabled(false); 191 validator.validate(certPath, params); 192 // Add intermediate CAs to the index to tolerate sites 193 // that assume that the browser will have cached these. 194 // The server certificate is skipped by skipping the 195 // zeroth element of new chain and note that the root CA 196 // will have been removed in 197 // cleanupCertChainAndFindTrustAnchors. http://b/3404902 198 for (int i = 1; i < newChain.length; i++) { 199 trustedCertificateIndex.index(newChain[i]); 200 } 201 } catch (InvalidAlgorithmParameterException e) { 202 throw new CertificateException(e); 203 } catch (CertPathValidatorException e) { 204 throw new CertificateException(e); 205 } 206 } 207 208 /** 209 * Clean up the certificate chain, returning a cleaned up chain, 210 * which may be a new array instance if elements were removed. 211 * Theoretically, we shouldn't have to do this, but various web 212 * servers in practice are mis-configured to have out-of-order 213 * certificates, expired self-issued root certificate, or CAs with 214 * unsupported signature algorithms such as 215 * md2WithRSAEncryption. This also handles removing old certs 216 * after bridge CA certs. 217 */ 218 private X509Certificate[] cleanupCertChainAndFindTrustAnchors(X509Certificate[] chain, 219 Set<TrustAnchor> trustAnchors) { 220 X509Certificate[] original = chain; 221 222 // 1. Clean the received certificates chain. 223 int currIndex; 224 // Start with the first certificate in the chain, assuming it 225 // is the leaf certificate (server or client cert). 226 for (currIndex = 0; currIndex < chain.length; currIndex++) { 227 // If the current cert is a TrustAnchor, we can ignore the rest of the chain. 228 // This avoids including "bridge" CA certs that added for legacy compatability. 229 TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[currIndex]); 230 if (trustAnchor != null) { 231 trustAnchors.add(trustAnchor); 232 currIndex--; 233 break; 234 } 235 // Walk the rest of the chain to find a "subject" matching 236 // the "issuer" of the current certificate. In a properly 237 // order chain this should be the next cert and be fast. 238 // If not, we reorder things to be as the validator will 239 // expect. 240 boolean foundNext = false; 241 for (int nextIndex = currIndex + 1; nextIndex < chain.length; nextIndex++) { 242 if (chain[currIndex].getIssuerDN().equals(chain[nextIndex].getSubjectDN())) { 243 foundNext = true; 244 // Exchange certificates so that 0 through currIndex + 1 are in proper order 245 if (nextIndex != currIndex + 1) { 246 // don't mutuate original chain, which may be directly from an SSLSession 247 if (chain == original) { 248 chain = original.clone(); 249 } 250 X509Certificate tempCertificate = chain[nextIndex]; 251 chain[nextIndex] = chain[currIndex + 1]; 252 chain[currIndex + 1] = tempCertificate; 253 } 254 break; 255 } 256 } 257 // If we can't find the next in the chain, just give up 258 // and use what we found so far. This drops unrelated 259 // certificates that have nothing to do with the cert 260 // chain. 261 if (!foundNext) { 262 break; 263 } 264 } 265 266 // 2. If the chain is now shorter, copy to an appropriately sized array. 267 int chainLength = currIndex + 1; 268 X509Certificate[] newChain = ((chainLength == chain.length) 269 ? chain 270 : Arrays.copyOf(chain, chainLength)); 271 272 // 3. If no TrustAnchor was found in cleanup, look for one now 273 if (trustAnchors.isEmpty()) { 274 TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[chainLength-1]); 275 if (trustAnchor != null) { 276 trustAnchors.add(trustAnchor); 277 } 278 } 279 return newChain; 280 } 281 282 /** 283 * Check the trustedCertificateIndex for the cert to see if it is 284 * already trusted and failing that check the KeyStore if it is 285 * available. 286 */ 287 private TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) { 288 TrustAnchor trustAnchor = trustedCertificateIndex.findBySubjectAndPublicKey(cert); 289 if (trustAnchor != null) { 290 return trustAnchor; 291 } 292 if (trustedCertificateStore == null) { 293 // not trusted and no TrustedCertificateStore to check 294 return null; 295 } 296 // probe KeyStore for a cert. AndroidCAStore stores its 297 // contents hashed by cert subject on the filesystem to make 298 // this faster than scanning all key store entries. 299 if (trustedCertificateStore.isTrustAnchor(cert)) { 300 // add new TrustAnchor to params index to avoid 301 // checking filesystem next time around. 302 return trustedCertificateIndex.index(cert); 303 } 304 return null; 305 } 306 307 private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert) { 308 TrustAnchor trustAnchor = trustedCertificateIndex.findByIssuerAndSignature(lastCert); 309 if (trustAnchor != null) { 310 return trustAnchor; 311 } 312 if (trustedCertificateStore == null) { 313 return null; 314 } 315 // we have a KeyStore and the issuer of the last cert in 316 // the chain seems to be missing from the 317 // TrustedCertificateIndex, check the KeyStore for a hit 318 X509Certificate issuer = trustedCertificateStore.findIssuer(lastCert); 319 if (issuer != null) { 320 return trustedCertificateIndex.index(issuer); 321 } 322 return null; 323 } 324 325 @Override public X509Certificate[] getAcceptedIssuers() { 326 return (acceptedIssuers != null) ? acceptedIssuers.clone() : acceptedIssuers(rootKeyStore); 327 } 328 } 329