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