    001/*
    002 * ====================================================================
    003 *
    004 *  Copyright 1999-2006 The Apache Software Foundation
    005 *
    006 *  Licensed under the Apache License, Version 2.0 (the "License");
    007 *  you may not use this file except in compliance with the License.
    008 *  You may obtain a copy of the License at
    009 *
    010 *      http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 *  Unless required by applicable law or agreed to in writing, software
    013 *  distributed under the License is distributed on an "AS IS" BASIS,
    014 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    015 *  See the License for the specific language governing permissions and
    016 *  limitations under the License.
    017 * ====================================================================
    018 *
    019 * This software consists of voluntary contributions made by many
    020 * individuals on behalf of the Apache Software Foundation.  For more
    021 * information on the Apache Software Foundation, please see
    022 * <http://www.apache.org/>.
    023 *
    024 */
    025
    026package org.apache.commons.httpclient.contrib.ssl;
    027
    028import org.apache.commons.ssl.HttpSecureProtocol;
    029import org.apache.commons.ssl.KeyMaterial;
    030import org.apache.commons.ssl.TrustMaterial;
    031
    032import java.io.IOException;
    033import java.net.Socket;
    034import java.security.GeneralSecurityException;
    035import java.security.KeyManagementException;
    036import java.security.KeyStoreException;
    037import java.security.NoSuchAlgorithmException;
    038import java.security.cert.CertificateException;
    039
    040/**
    041 * <p/>
    042 * TrustSSLProtocolSocketFactory allows you exercise full control over the
    043 * HTTPS server certificates you are going to trust.  Instead of relying
    044 * on the Certificate Authorities already present in "jre/lib/security/cacerts",
    045 * TrustSSLProtocolSocketFactory only trusts the public certificates you provide
    046 * to its constructor.
    047 * </p>
    048 * <p/>
    049 * TrustSSLProtocolSocketFactory can be used to create SSL {@link Socket}s
    050 * that accepts self-signed certificates.  Unlike EasySSLProtocolSocketFactory,
    051 * TrustSSLProtocolSocketFactory can be used in production.  This is because
    052 * it forces you to pre-install the self-signed certificate you are going to
    053 * trust locally.
    054 * <p/>
    055 * TrustSSLProtocolSocketFactory can parse both Java Keystore Files (*.jks)
    056 * and base64 PEM encoded public certificates (*.pem).
    057 * </p>
    058 * <p/>
    059 * Example of using TrustSSLProtocolSocketFactory
    060 * <pre>
    061 * 1.  First we must find the certificate we want to trust.  In this example
    062 *     we'll use gmail.google.com's certificate.
    063 * <p/>
    064 *   openssl s_client -showcerts -connect gmail.google.com:443
    065 * <p/>
    066 * 2.  Cut & paste into a "cert.pem" any certificates you are interested in
    067 *     trusting in accordance with your security policies.  In this example I'll
    068 *     actually use the current "gmail.google.com" certificate (instead of the
    069 *     Thawte CA certificate that signed the gmail certificate - that would be
    070 *     too boring) - but it expires on June 7th, 2006, so this example won't be
    071 *     useful for very long!
    072 * <p/>
    073 * Here's what my "cert.pem" file looks like:
    074 * <p/>
    075 * -----BEGIN CERTIFICATE-----
    076 * MIIDFjCCAn+gAwIBAgIDP3PeMA0GCSqGSIb3DQEBBAUAMEwxCzAJBgNVBAYTAlpB
    077 * MSUwIwYDVQQKExxUaGF3dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMRYwFAYDVQQD
    078 * Ew1UaGF3dGUgU0dDIENBMB4XDTA1MDYwNzIyMTI1N1oXDTA2MDYwNzIyMTI1N1ow
    079 * ajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
    080 * dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJbmMxGTAXBgNVBAMTEGdtYWls
    081 * Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALoRiWYW0hZw
    082 * 9TSn3s9912syZg1CP2TaC86PU1Ao2qf3pVu7Mx10Wl8W+aKZrQlvrYjTwku4sEh+
    083 * 9uI+gWnfmCd0OyVcXr1eFOGCYiiyaPv79Wtb0m0d8GuiRSJhYkZGzGlgFViws2vR
    084 * BAMCD2fdp7WGJUVGYOO+s52dgAMUHQXxAgMBAAGjgecwgeQwKAYDVR0lBCEwHwYI
    085 * KwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwNgYDVR0fBC8wLTAroCmgJ4Yl
    086 * aHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVNHQ0NBLmNybDByBggrBgEFBQcB
    087 * AQRmMGQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0ZS5jb20wPgYIKwYB
    088 * BQUHMAKGMmh0dHA6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0b3J5L1RoYXd0ZV9T
    089 * R0NfQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEEBQADgYEAktM1l1cV
    090 * ebi+Uo6fCE/eLnvvY6QbNNCsU5Pi9B5E1BlEUG+AGpgzE2cSPw1N4ZZb+2AWWwjx
    091 * H8/IrJ143KZZXM49ri3Z2e491Jj8qitrMauT7/hb16Jw6I02/74/do4TtHu/Eifr
    092 * EZCaSOobSHGeufHjlqlC3ehC4Bx4mLexIMk=
    093 * -----END CERTIFICATE-----
    094 * <p/>
    095 * 3.  Run "openssl x509" to analyze the certificate more deeply.  This helps
    096 *     us answer questions like "Do we really want to trust it?  When does it
    097 *     expire? What's the value of the CN (Common Name) field?".
    098 * <p/>
    099 *     "openssl x509" is also super cool, and will impress all your friends,
    100 *     coworkers, family, and that cute girl at the starbucks.   :-)
    101 * <p/>
    102 *     If you dig through "man x509" you'll find this example.  Run it:
    103 * <p/>
    104 *    openssl x509 -in cert.pem -noout -text
    105 * <p/>
    106 * 4.  Rename "cert.pem" to "gmail.pem" so that step 5 works.
    107 * <p/>
    108 * 5.  Setup the TrustSSLProtocolSocketFactory to trust "gmail.google.com"
    109 *     for URLS of the form "https-gmail://" - but don't trust anything else
    110 *     when using "https-gmail://":
    111 * <p/>
    112 *     TrustSSLProtocolSocketFactory sf = new TrustSSLProtocolSocketFactory( "/path/to/gmail.pem" );
    113 *     Protocol trustHttps = new Protocol("https-gmail", sf, 443);
    114 *     Protocol.registerProtocol("https-gmail", trustHttps);
    115 * <p/>
    116 *     HttpClient client = new HttpClient();
    117 *     GetMethod httpget = new GetMethod("https-gmail://gmail.google.com/");
    118 *     client.executeMethod(httpget);
    119 * <p/>
    120 * 6.  Notice that "https-gmail://" cannot connect to "www.wellsfargo.com" -
    121 *     the server's certificate isn't trusted!  It would still work using
    122 *     regular "https://" because Java would use the "jre/lib/security/cacerts"
    123 *     file.
    124 * <p/>
    125 *     httpget = new GetMethod("https-gmail://www.wellsfargo.com/");
    126 *     client.executeMethod(httpget);
    127 * <p/>
    128 * javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
    129 * <p/>
    130 * <p/>
    131 * 7.  Of course "https-gmail://" cannot connect to hosts where the CN field
    132 *     in the certificate doesn't match the hostname.  The same is supposed to
    133 *     be true of regular "https://", but HTTPClient is a bit lenient.
    134 * <p/>
    135 *     httpget = new GetMethod("https-gmail://gmail.com/");
    136 *     client.executeMethod(httpget);
    137 * <p/>
    138 * javax.net.ssl.SSLException: hostname in certificate didn't match: &lt;gmail.com> != &lt;gmail.google.com>
    139 * <p/>
    140 * <p/>
    141 * 8.  You can use "*.jks" files instead of "*.pem" if you prefer.  Use the 2nd constructor
    142 *     in that case to pass along the JKS password:
    143 * <p/>
    144 *   new TrustSSLProtocolSocketFactory( "/path/to/gmail.jks", "my_password".toCharArray() );
    145 * <p/>
    146 * </pre>
    147 *
    148 * @author Credit Union Central of British Columbia
    149 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
    150 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
    151 *         <p/>
    152 *         <p/>
    153 *         DISCLAIMER: HttpClient developers DO NOT actively support this component.
    154 *         The component is provided as a reference material, which may be inappropriate
    155 *         for use without additional customization.
    156 *         </p>
    157 * @since 17-Feb-2006
    158 */
    159
    160public class TrustSSLProtocolSocketFactory extends HttpSecureProtocol {
    161
    162    /**
    163     * @param pathToTrustStore Path to either a ".jks" Java Key Store, or a
    164     *                         ".pem" base64 encoded certificate.  If it's a
    165     *                         ".pem" base64 certificate, the file must start
    166     *                         with "------BEGIN CERTIFICATE-----", and must end
    167     *                         with "-------END CERTIFICATE--------".
    168     */
    169    public TrustSSLProtocolSocketFactory(String pathToTrustStore)
    170        throws GeneralSecurityException, IOException {
    171        this(pathToTrustStore, null);
    172    }
    173
    174    /**
    175     * @param pathToTrustStore Path to either a ".jks" Java Key Store, or a
    176     *                         ".pem" base64 encoded certificate.  If it's a
    177     *                         ".pem" base64 certificate, the file must start
    178     *                         with "------BEGIN CERTIFICATE-----", and must end
    179     *                         with "-------END CERTIFICATE--------".
    180     * @param password         Password to open the ".jks" file.  If "truststore"
    181     *                         is a ".pem" file, then password can be null; if
    182     *                         password isn't null and we're using a ".pem" file,
    183     *                         then technically, this becomes the password to
    184     *                         open up the special in-memory keystore we create
    185     *                         to hold the ".pem" file, but it's not important at
    186     *                         all.
    187     * @throws CertificateException
    188     * @throws KeyStoreException
    189     * @throws IOException
    190     * @throws NoSuchAlgorithmException
    191     * @throws KeyManagementException
    192     */
    193    public TrustSSLProtocolSocketFactory(String pathToTrustStore, char[] password)
    194        throws GeneralSecurityException, IOException {
    195        super();
    196        TrustMaterial tm;
    197        try {
    198            tm = new KeyMaterial(pathToTrustStore, password);
    199        } catch (KeyStoreException kse) {
    200            // KeyMaterial constructor blows up in no keys found,
    201            // so we fall back to TrustMaterial constructor instead.
    202            tm = new TrustMaterial(pathToTrustStore, password);
    203        }
    204        super.setTrustMaterial(tm);
    205    }
    206
    207}