001/*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.15/src/java/org/apache/commons/ssl/TrustMaterial.java $
003 * $Revision: 171 $
004 * $Date: 2014-05-09 08:15:26 -0700 (Fri, 09 May 2014) $
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.File;
035import java.io.FileInputStream;
036import java.io.IOException;
037import java.io.InputStream;
038import java.net.URL;
039import java.security.GeneralSecurityException;
040import java.security.KeyStore;
041import java.security.KeyStoreException;
042import java.security.cert.Certificate;
043import java.security.cert.X509Certificate;
044import java.util.Arrays;
045import java.util.Collection;
046import java.util.Collections;
047import java.util.Enumeration;
048import 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 */
056public 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    public TrustMaterial(byte[] jks, char[] password)
209        throws GeneralSecurityException, IOException {
210
211        KeyStoreBuilder.BuildResult br;
212        br = KeyStoreBuilder.parse(jks, password, null, true);
213        if (br.jks != null) {
214            // If we've been given a keystore, just use that.
215            this.jks = br.jks;
216        } else {
217            // Otherwise we need to build a keystore from what we were given.
218            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
219            if (br.chains != null && !br.chains.isEmpty()) {
220                Certificate[] c = (Certificate[]) br.chains.get(0);
221                if (c.length > 0) {
222                    ks.load(null, password);
223                    loadCerts(ks, Arrays.asList(c));
224                }
225            }
226            this.jks = ks;
227        }
228
229        // Should validate our keystore to make sure it has at least ONE
230        // certificate entry:
231        KeyStore ks = this.jks;
232        boolean hasCertificates = false;
233        Enumeration en = ks.aliases();
234        while (en.hasMoreElements()) {
235            String alias = (String) en.nextElement();
236            if (ks.isCertificateEntry(alias)) {
237                hasCertificates = true;
238                break;
239            }
240        }
241        if (!hasCertificates) {
242            throw new KeyStoreException("TrustMaterial couldn't load any certificates to trust!");
243        }
244
245        addTrustMaterial(this);
246
247        // We're not a simple trust type, so set value to 0.
248        // Only TRUST_ALL and TRUST_THIS_JVM are simple trust types.
249        this.simpleTrustType = 0;
250    }
251
252    public KeyStore getKeyStore() {
253        return jks;
254    }
255
256    private static void loadCerts(KeyStore ks, Collection certs)
257        throws KeyStoreException {
258        Iterator it = certs.iterator();
259        int count = 0;
260        while (it.hasNext()) {
261            X509Certificate cert = (X509Certificate) it.next();
262
263            // I could be fancy and parse out the CN field from the
264            // certificate's subject, but these names don't actually matter
265            // at all - I think they just have to be unique.
266            String cn = Certificates.getCN(cert);
267            String alias = cn + "_" + count;
268            ks.setCertificateEntry(alias, cert);
269            count++;
270        }
271    }
272
273    protected boolean containsTrustAll() {
274        boolean yes = this.simpleTrustType == SIMPLE_TRUST_TYPE_TRUST_ALL;
275        if ( !yes ) {
276            yes = super.containsTrustAll();
277        }
278        return yes;
279    }
280
281}