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.CertificateEncodingException; 27 import java.security.cert.CertificateException; 28 import java.security.cert.CertificateFactory; 29 import java.security.cert.PKIXParameters; 30 import java.security.cert.TrustAnchor; 31 import java.security.cert.X509Certificate; 32 import java.util.Arrays; 33 import java.util.Enumeration; 34 import java.util.HashSet; 35 import java.util.Iterator; 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 class TrustManagerImpl implements X509TrustManager { 48 49 private CertPathValidator validator; 50 51 private PKIXParameters params; 52 53 private Exception err = null; 54 55 private CertificateFactory factory; 56 57 /** 58 * Creates trust manager implementation 59 * 60 * @param ks 61 */ 62 public TrustManagerImpl(KeyStore ks) { 63 try { 64 validator = CertPathValidator.getInstance("PKIX"); 65 factory = CertificateFactory.getInstance("X509"); 66 byte[] nameConstrains = null; 67 Set<TrustAnchor> trusted = new HashSet<TrustAnchor>(); 68 for (Enumeration<String> en = ks.aliases(); en.hasMoreElements();) { 69 final String alias = en.nextElement(); 70 final X509Certificate cert = (X509Certificate) ks.getCertificate(alias); 71 if (cert != null) { 72 trusted.add(new TrustAnchor(cert, nameConstrains)); 73 } 74 } 75 params = new PKIXParameters(trusted); 76 params.setRevocationEnabled(false); 77 } catch (Exception e) { 78 err = e; 79 } 80 } 81 82 // BEGIN android-added 83 /** 84 * Indexes trust anchors so they can be found in O(1) instead of O(N) time. 85 */ 86 public void indexTrustAnchors() throws CertificateEncodingException, 87 InvalidAlgorithmParameterException, KeyStoreException { 88 params = new IndexedPKIXParameters(params.getTrustAnchors()); 89 params.setRevocationEnabled(false); 90 } 91 // END android-added 92 93 /** 94 * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[], 95 * String) 96 */ 97 public void checkClientTrusted(X509Certificate[] chain, String authType) 98 throws CertificateException { 99 if (chain == null || chain.length == 0 || authType == null 100 || authType.length() == 0) { 101 throw new IllegalArgumentException("null or zero-length parameter"); 102 } 103 if (err != null) { 104 throw new CertificateException(err); 105 } 106 // BEGIN android-added 107 // Caters to degenerate special case where we can't 108 // establish an actual certificate chain the usual way, 109 // but have the peer certificate in our trust store. 110 if (isDirectlyTrustedCert(chain)) { 111 return; 112 } 113 // END android-added 114 try { 115 // BEGIN android-changed 116 CertPath certPath = factory.generateCertPath(Arrays.asList(chain)); 117 if (!Arrays.equals(chain[0].getEncoded(), 118 ((X509Certificate)certPath.getCertificates().get(0)) 119 .getEncoded())) { 120 // Sanity check failed (shouldn't ever happen, but we 121 // are using pretty remote code) 122 throw new CertificateException("Certificate chain error"); 123 } 124 validator.validate(certPath, params); 125 // END android-changed 126 } catch (InvalidAlgorithmParameterException e) { 127 throw new CertificateException(e); 128 } catch (CertPathValidatorException e) { 129 throw new CertificateException(e); 130 } 131 } 132 133 /** 134 * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], 135 * String) 136 */ 137 public void checkServerTrusted(X509Certificate[] chain, String authType) 138 throws CertificateException { 139 if (chain == null || chain.length == 0 || authType == null 140 || authType.length() == 0) { 141 throw new IllegalArgumentException("null or zero-length parameter"); 142 } 143 if (err != null) { 144 throw new CertificateException(err); 145 } 146 // BEGIN android-changed 147 CertificateException ce = null; 148 try { 149 CertPath certPath = factory.generateCertPath(Arrays.asList(chain)); 150 if (!Arrays.equals(chain[0].getEncoded(), 151 certPath.getCertificates().get(0).getEncoded())) { 152 // Sanity check failed (shouldn't ever happen, but we 153 // are using pretty remote code) 154 throw new CertificateException("Certificate chain error"); 155 } 156 validator.validate(certPath, params); 157 // END android-changed 158 } catch (InvalidAlgorithmParameterException e) { 159 ce = new CertificateException(e); 160 } catch (CertPathValidatorException e) { 161 ce = new CertificateException(e); 162 } 163 // BEGIN android-added 164 if (ce != null) { 165 // Caters to degenerate special case where we can't 166 // establish an actual certificate chain the usual way 167 // but have the peer certificate in our trust store. 168 if (!isDirectlyTrustedCert(chain)) { 169 throw ce; 170 } 171 } 172 // END android-added 173 } 174 175 /** 176 * Checks whether the given chain is just a certificate 177 * that we have in our trust store. 178 * 179 * @param chain The certificate chain. 180 * 181 * @return True if the certificate is in our trust store, false otherwise. 182 */ 183 private boolean isDirectlyTrustedCert(X509Certificate[] chain) { 184 byte[] questionable; 185 186 if (chain.length == 1) { 187 if (params instanceof IndexedPKIXParameters) { 188 IndexedPKIXParameters index = (IndexedPKIXParameters) params; 189 return index.isDirectlyTrusted(chain[0]); 190 } else { 191 try { 192 questionable = chain[0].getEncoded(); 193 Set<TrustAnchor> anchors = params.getTrustAnchors(); 194 195 for (TrustAnchor trustAnchor : anchors) { 196 byte[] trusted = trustAnchor.getTrustedCert() 197 .getEncoded(); 198 if (Arrays.equals(questionable, trusted)) { 199 return true; 200 } 201 } 202 } catch (CertificateEncodingException e) { 203 // Ignore. 204 } 205 } 206 207 } 208 209 return false; 210 } 211 // END android-changed 212 213 /** 214 * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() 215 */ 216 public X509Certificate[] getAcceptedIssuers() { 217 if (params == null) { 218 return new X509Certificate[0]; 219 } 220 Set<TrustAnchor> anchors = params.getTrustAnchors(); 221 X509Certificate[] certs = new X509Certificate[anchors.size()]; 222 int i = 0; 223 for (Iterator<TrustAnchor> it = anchors.iterator(); it.hasNext();) { 224 certs[i++] = it.next().getTrustedCert(); 225 } 226 return certs; 227 } 228 229 } 230