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