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