    001/*
    002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.15/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}