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: <gmail.com> != <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 }