    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}