001    /*
002     * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/src/java/org/apache/commons/ssl/TrustMaterial.java $
003     * $Revision: 148 $
004     * $Date: 2009-05-25 23:07:21 -0700 (Mon, 25 May 2009) $
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.File;
035    import java.io.FileInputStream;
036    import java.io.IOException;
037    import java.io.InputStream;
038    import java.net.URL;
039    import java.security.GeneralSecurityException;
040    import java.security.KeyStore;
041    import java.security.KeyStoreException;
042    import java.security.cert.Certificate;
043    import java.security.cert.X509Certificate;
044    import java.util.Arrays;
045    import java.util.Collection;
046    import java.util.Collections;
047    import java.util.Enumeration;
048    import java.util.Iterator;
049    
050    /**
051     * @author Credit Union Central of British Columbia
052     * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
053     * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
054     * @since 27-Feb-2006
055     */
056    public class TrustMaterial extends TrustChain {
057        final static int SIMPLE_TRUST_TYPE_TRUST_ALL = 1;
058        final static int SIMPLE_TRUST_TYPE_TRUST_THIS_JVM = 2;
059    
060        /**
061         * Might be null if "$JAVA_HOME/jre/lib/security/cacerts" doesn't exist.
062         */
063        public final static TrustMaterial CACERTS;
064    
065        /**
066         * Might be null if "$JAVA_HOME/jre/lib/security/jssecacerts" doesn't exist.
067         */
068        public final static TrustMaterial JSSE_CACERTS;
069    
070        /**
071         * Should never be null (unless both CACERTS and JSSE_CACERTS are not
072         * present???).  Is either CACERTS or JSSE_CACERTS.  Priority given to
073         * JSSE_CACERTS, but 99.9% of the time it's CACERTS, since JSSE_CACERTS
074         * is almost never present.
075         */
076        public final static TrustMaterial DEFAULT;
077    
078        static {
079            JavaImpl.load();
080            String javaHome = System.getProperty("java.home");
081            String pathToCacerts = javaHome + "/lib/security/cacerts";
082            String pathToJSSECacerts = javaHome + "/lib/security/jssecacerts";
083            TrustMaterial cacerts = null;
084            TrustMaterial jssecacerts = null;
085            try {
086                File f = new File(pathToCacerts);
087                if (f.exists()) {
088                    cacerts = new TrustMaterial(pathToCacerts);
089                }
090            }
091            catch (Exception e) {
092                e.printStackTrace();
093            }
094            try {
095                File f = new File(pathToJSSECacerts);
096                if (f.exists()) {
097                    jssecacerts = new TrustMaterial(pathToJSSECacerts);
098                }
099            }
100            catch (Exception e) {
101                e.printStackTrace();
102            }
103    
104            CACERTS = cacerts;
105            JSSE_CACERTS = jssecacerts;
106            if (JSSE_CACERTS != null) {
107                DEFAULT = JSSE_CACERTS;
108            } else {
109                DEFAULT = CACERTS;
110            }
111        }
112    
113        public final static TrustMaterial TRUST_ALL =
114            new TrustMaterial(SIMPLE_TRUST_TYPE_TRUST_ALL);
115    
116        public final static TrustMaterial TRUST_THIS_JVM =
117            new TrustMaterial(SIMPLE_TRUST_TYPE_TRUST_THIS_JVM);
118    
119        public final int simpleTrustType;
120        private final KeyStore jks;
121    
122        private TrustMaterial(int simpleTrustType) {
123            this(null, simpleTrustType);
124        }
125    
126        TrustMaterial(KeyStore jks, int simpleTrustType) {
127            if (jks == null && simpleTrustType != 0) {
128                // Just use CACERTS as a place holder, since Java 5 and 6 seem to get
129                // upset when we hand SSLContext null TrustManagers.  See
130                // Java14.initSSL(), which despite its name, is also used
131                // with Java5 and Java6.
132                this.jks = CACERTS != null ? CACERTS.jks : JSSE_CACERTS.jks;
133            } else {
134                this.jks = jks;
135            }
136            addTrustMaterial(this);
137            this.simpleTrustType = simpleTrustType;
138        }
139    
140        public TrustMaterial(Collection x509Certs)
141            throws GeneralSecurityException, IOException {
142            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
143            ks.load(null, null);
144            loadCerts(ks, x509Certs);
145            this.jks = ks;
146            addTrustMaterial(this);
147    
148            // We're not a simple trust type, so set value to 0.
149            // Only TRUST_ALL and TRUST_THIS_JVM are simple trust types.
150            this.simpleTrustType = 0;
151        }
152    
153        public TrustMaterial(X509Certificate x509Cert)
154            throws GeneralSecurityException, IOException {
155            this(Collections.singleton(x509Cert));
156        }
157    
158        public TrustMaterial(X509Certificate[] x509Certs)
159            throws GeneralSecurityException, IOException {
160            this(Arrays.asList(x509Certs));
161        }
162    
163        public TrustMaterial(byte[] pemBase64)
164            throws GeneralSecurityException, IOException {
165            this(pemBase64, null);
166        }
167    
168        public TrustMaterial(InputStream pemBase64)
169            throws GeneralSecurityException, IOException {
170            this(Util.streamToBytes(pemBase64));
171        }
172    
173        public TrustMaterial(String pathToPemFile)
174            throws GeneralSecurityException, IOException {
175            this(new FileInputStream(pathToPemFile));
176        }
177    
178        public TrustMaterial(File pemFile)
179            throws GeneralSecurityException, IOException {
180            this(new FileInputStream(pemFile));
181        }
182    
183        public TrustMaterial(URL urlToPemFile)
184            throws GeneralSecurityException, IOException {
185            this(urlToPemFile.openStream());
186        }
187    
188        public TrustMaterial(String pathToJksFile, char[] password)
189            throws GeneralSecurityException, IOException {
190            this(new File(pathToJksFile), password);
191        }
192    
193        public TrustMaterial(File jksFile, char[] password)
194            throws GeneralSecurityException, IOException {
195            this(new FileInputStream(jksFile), password);
196        }
197    
198        public TrustMaterial(URL urlToJKS, char[] password)
199            throws GeneralSecurityException, IOException {
200            this(urlToJKS.openStream(), password);
201        }
202    
203        public TrustMaterial(InputStream jks, char[] password)
204            throws GeneralSecurityException, IOException {
205            this(Util.streamToBytes(jks), password);
206        }
207    
208    
209        public TrustMaterial(byte[] jks, char[] password)
210            throws GeneralSecurityException, IOException {
211    
212            KeyStoreBuilder.BuildResult br;
213            br = KeyStoreBuilder.parse(jks, password, null);
214            if (br.jks != null) {
215                // If we've been given a keystore, just use that.
216                this.jks = br.jks;
217            } else {
218                // Otherwise we need to build a keystore from what we were given.
219                KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
220                if (br.chains != null && !br.chains.isEmpty()) {
221                    Certificate[] c = (Certificate[]) br.chains.get(0);
222                    if (c.length > 0) {
223                        ks.load(null, password);
224                        loadCerts(ks, Arrays.asList(c));
225                    }
226                }
227                this.jks = ks;
228            }
229    
230            // Should validate our keystore to make sure it has at least ONE
231            // certificate entry:
232            KeyStore ks = this.jks;
233            boolean hasCertificates = false;
234            Enumeration en = ks.aliases();
235            while (en.hasMoreElements()) {
236                String alias = (String) en.nextElement();
237                if (ks.isCertificateEntry(alias)) {
238                    hasCertificates = true;
239                    break;
240                }
241            }
242            if (!hasCertificates) {
243                throw new KeyStoreException("TrustMaterial couldn't load any certificates to trust!");
244            }
245    
246            addTrustMaterial(this);
247    
248            // We're not a simple trust type, so set value to 0.
249            // Only TRUST_ALL and TRUST_THIS_JVM are simple trust types.
250            this.simpleTrustType = 0;
251        }
252    
253        public KeyStore getKeyStore() {
254            return jks;
255        }
256    
257        private static void loadCerts(KeyStore ks, Collection certs)
258            throws KeyStoreException {
259            Iterator it = certs.iterator();
260            int count = 0;
261            while (it.hasNext()) {
262                X509Certificate cert = (X509Certificate) it.next();
263    
264                // I could be fancy and parse out the CN field from the
265                // certificate's subject, but these names don't actually matter
266                // at all - I think they just have to be unique.
267                String cn = Certificates.getCN(cert);
268                String alias = cn + "_" + count;
269                ks.setCertificateEntry(alias, cert);
270                count++;
271            }
272        }
273    
274        protected boolean containsTrustAll() {
275            boolean yes = this.simpleTrustType == SIMPLE_TRUST_TYPE_TRUST_ALL;
276            if ( !yes ) {
277                yes = super.containsTrustAll();
278            }
279            return yes;
280        }
281    
282    }