001    /*
002     * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/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    
032    package org.apache.commons.ssl;
033    
034    import java.io.IOException;
035    import java.security.KeyStore;
036    import java.security.KeyStoreException;
037    import java.security.NoSuchAlgorithmException;
038    import java.security.cert.CertificateException;
039    import java.security.cert.X509Certificate;
040    import java.util.Collections;
041    import java.util.Enumeration;
042    import java.util.HashSet;
043    import java.util.Iterator;
044    import java.util.Set;
045    import java.util.SortedSet;
046    import 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     */
054    public 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    }