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