001/*
002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/src/java/org/apache/commons/ssl/TrustChain.java $
003 * $Revision: 138 $
004 * $Date: 2008-03-03 23:50:07 -0800 (Mon, 03 Mar 2008) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation.  For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032package org.apache.commons.ssl;
033
034import java.io.IOException;
035import java.security.KeyStore;
036import java.security.KeyStoreException;
037import java.security.NoSuchAlgorithmException;
038import java.security.cert.CertificateException;
039import java.security.cert.X509Certificate;
040import java.util.Collections;
041import java.util.Enumeration;
042import java.util.HashSet;
043import java.util.Iterator;
044import java.util.Set;
045import java.util.SortedSet;
046import java.util.TreeSet;
047
048/**
049 * @author Credit Union Central of British Columbia
050 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
051 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
052 * @since 27-Feb-2006
053 */
054public class TrustChain {
055    private final Set trustMaterial =
056        Collections.synchronizedSet(new HashSet());
057    private SortedSet x509Certificates = null;
058    private KeyStore unifiedKeyStore = null;
059
060    public TrustChain() {
061    }
062
063    public synchronized KeyStore getUnifiedKeyStore()
064        throws KeyStoreException, IOException, NoSuchAlgorithmException,
065        CertificateException {
066
067        // x509Certificates serves as our "cache available" indicator.
068        if (x509Certificates != null) {
069            return unifiedKeyStore;
070        }
071
072        // First, extract all the X509Certificates from this TrustChain.
073        this.x509Certificates = new TreeSet(Certificates.COMPARE_BY_EXPIRY);
074        Iterator it = trustMaterial.iterator();
075        while (it.hasNext()) {
076            TrustMaterial tm = (TrustMaterial) it.next();
077            KeyStore ks = tm.getKeyStore();
078            if (ks != null) {
079                Enumeration en = ks.aliases();
080                while (en.hasMoreElements()) {
081                    String alias = (String) en.nextElement();
082                    if (ks.isCertificateEntry(alias)) {
083                        X509Certificate cert;
084                        cert = (X509Certificate) ks.getCertificate(alias);
085                        if (!x509Certificates.contains(cert)) {
086                            x509Certificates.add(cert);
087                        }
088                    }
089                }
090            }
091        }
092
093        // Now that the X509Certificates are extracted, create the unified
094        // keystore.
095        it = x509Certificates.iterator();
096        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
097        ks.load(null, null);
098        int count = 0;
099        while (it.hasNext()) {
100            X509Certificate cert = (X509Certificate) it.next();
101            // The "count" should keep the aliases unique (is that important?)
102            String alias = "commons-ssl-" + count;
103            ks.setCertificateEntry(alias, cert);
104            count++;
105        }
106        this.unifiedKeyStore = ks;
107        return unifiedKeyStore;
108    }
109
110    public synchronized void addTrustMaterial(TrustChain tc) {
111        this.x509Certificates = null;  // invalidate cache
112        if (tc instanceof TrustMaterial) {
113            trustMaterial.add(tc);
114        }
115        // If duplicates are added, the Set will remove them.
116        trustMaterial.addAll(tc.trustMaterial);
117    }
118
119    public boolean contains(TrustChain tc) {
120        if (tc instanceof TrustMaterial) {
121            return trustMaterial.contains(tc);
122        } else {
123            return trustMaterial.containsAll(tc.trustMaterial);
124        }
125    }
126
127    public boolean contains(X509Certificate cert)
128        throws KeyStoreException, IOException, NoSuchAlgorithmException,
129        CertificateException {
130        return getCertificates().contains(cert);
131    }
132
133    public Object getTrustManagerFactory()
134        throws NoSuchAlgorithmException, KeyStoreException, IOException,
135        CertificateException {
136        KeyStore uks = getUnifiedKeyStore();
137        if (uks != null) {
138            return JavaImpl.newTrustManagerFactory(uks);
139        } else {
140            return null;
141        }
142    }
143
144    /**
145     * @return Array of TrustManager[] - presumably these will be dropped into
146     *         a call to SSLContext.init().  Note:  returns null if this
147     *         TrustChain doesn't contain anything to trust.
148     * @throws NoSuchAlgorithmException serious problems
149     * @throws KeyStoreException        serious problems
150     * @throws IOException              serious problems
151     * @throws CertificateException     serious problems
152     */
153    public Object[] getTrustManagers()
154        throws NoSuchAlgorithmException, KeyStoreException, IOException,
155        CertificateException {
156        Object tmf = getTrustManagerFactory();
157        return tmf != null ? JavaImpl.getTrustManagers(tmf) : null;
158    }
159
160    /**
161     * @return All X509Certificates contained in this TrustChain as a SortedSet.
162     *         The X509Certificates are sorted based on expiry date.
163     *         <p/>
164     *         See org.apache.commons.ssl.Certificates.COMPARE_BY_EXPIRY.
165     * @throws KeyStoreException        serious problems
166     * @throws IOException              serious problems
167     * @throws NoSuchAlgorithmException serious problems
168     * @throws CertificateException     serious problems
169     */
170    public synchronized SortedSet getCertificates()
171        throws KeyStoreException, IOException, NoSuchAlgorithmException,
172        CertificateException {
173        if (x509Certificates == null) {
174            getUnifiedKeyStore();
175        }
176        return Collections.unmodifiableSortedSet(x509Certificates);
177    }
178
179    /**
180     * @return Count of all X509Certificates contained in this TrustChain.
181     * @throws KeyStoreException
182     * @throws IOException
183     * @throws NoSuchAlgorithmException
184     * @throws CertificateException
185     */
186    public synchronized int getSize()
187        throws KeyStoreException, IOException, NoSuchAlgorithmException,
188        CertificateException {
189        return getCertificates().size();
190    }
191
192    /**
193     * @return Count of all X509Certificates contained in this TrustChain.
194     * @throws KeyStoreException
195     * @throws IOException
196     * @throws NoSuchAlgorithmException
197     * @throws CertificateException
198     */
199    public synchronized boolean isEmpty()
200        throws KeyStoreException, IOException, NoSuchAlgorithmException,
201        CertificateException {
202        return getCertificates().isEmpty();
203    }
204
205    protected boolean containsTrustAll() {
206        Iterator it = trustMaterial.iterator();
207        while (it.hasNext()) {
208            TrustChain tc = (TrustChain) it.next();
209            if (tc == this) {
210                continue;
211            }
212            if (tc.containsTrustAll()) {
213                return true;
214            }
215        }
216        return false;
217    }
218
219}