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
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.X509Certificate;
043import java.util.Arrays;
044import java.util.Collection;
045import java.util.Collections;
046import java.util.Enumeration;
047import 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 */
055public 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}