001/*
002 * $Header$
003 * $Revision: 168 $
004 * $Date: 2014-05-06 16:25:46 -0700 (Tue, 06 May 2014) $
005 *
006 * ====================================================================
007 *
008 *  Copyright 2002-2006 The Apache Software Foundation
009 *
010 *  Licensed under the Apache License, Version 2.0 (the "License");
011 *  you may not use this file except in compliance with the License.
012 *  You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 *  Unless required by applicable law or agreed to in writing, software
017 *  distributed under the License is distributed on an "AS IS" BASIS,
018 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 *  See the License for the specific language governing permissions and
020 *  limitations under the License.
021 * ====================================================================
022 *
023 * This software consists of voluntary contributions made by many
024 * individuals on behalf of the Apache Software Foundation.  For more
025 * information on the Apache Software Foundation, please see
026 * <http://www.apache.org/>.
027 *
028 */
029
030package org.apache.commons.httpclient.contrib.ssl;
031
032import org.apache.commons.ssl.HttpSecureProtocol;
033import org.apache.commons.ssl.KeyMaterial;
034import org.apache.commons.ssl.TrustMaterial;
035
036import java.io.IOException;
037import java.net.URL;
038import java.security.GeneralSecurityException;
039import java.security.KeyStoreException;
040
041/**
042 * <p/>
043 * AuthSSLProtocolSocketFactory can be used to validate the identity of the HTTPS
044 * server against a list of trusted certificates and to authenticate to the HTTPS
045 * server using a private key.
046 * </p>
047 * <p/>
048 * <p/>
049 * AuthSSLProtocolSocketFactory will enable server authentication when supplied with
050 * a {@link java.security.KeyStore truststore} file containg one or several trusted certificates.
051 * The client secure socket will reject the connection during the SSL session handshake
052 * if the target HTTPS server attempts to authenticate itself with a non-trusted
053 * certificate.
054 * </p>
055 * <p/>
056 * <p/>
057 * Use JDK keytool utility to import a trusted certificate and generate a truststore file:
058 * <pre>
059 *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
060 *    </pre>
061 * </p>
062 * <p/>
063 * <p/>
064 * AuthSSLProtocolSocketFactory will enable client authentication when supplied with
065 * a {@link java.security.KeyStore keystore} file containg a private key/public certificate pair.
066 * The client secure socket will use the private key to authenticate itself to the target
067 * HTTPS server during the SSL session handshake if requested to do so by the server.
068 * The target HTTPS server will in its turn verify the certificate presented by the client
069 * in order to establish client's authenticity
070 * </p>
071 * <p/>
072 * <p/>
073 * Use the following sequence of actions to generate a keystore file
074 * </p>
075 * <ul>
076 * <li>
077 * <p/>
078 * Use JDK keytool utility to generate a new key
079 * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
080 * For simplicity use the same password for the key as that of the keystore
081 * </p>
082 * </li>
083 * <li>
084 * <p/>
085 * Issue a certificate signing request (CSR)
086 * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
087 * </p>
088 * </li>
089 * <li>
090 * <p/>
091 * Send the certificate request to the trusted Certificate Authority for signature.
092 * One may choose to act as her own CA and sign the certificate request using a PKI
093 * tool, such as OpenSSL.
094 * </p>
095 * </li>
096 * <li>
097 * <p/>
098 * Import the trusted CA root certificate
099 * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
100 * </p>
101 * </li>
102 * <li>
103 * <p/>
104 * Import the PKCS#7 file containg the complete certificate chain
105 * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
106 * </p>
107 * </li>
108 * <li>
109 * <p/>
110 * Verify the content the resultant keystore file
111 * <pre>keytool -list -v -keystore my.keystore</pre>
112 * </p>
113 * </li>
114 * </ul>
115 * <p/>
116 * Example of using custom protocol socket factory for a specific host:
117 * <pre>
118 *     Protocol authhttps = new Protocol("https",
119 *          new AuthSSLProtocolSocketFactory(
120 *              new URL("file:my.keystore"), "mypassword",
121 *              new URL("file:my.truststore"), "mypassword"), 443);
122 * <p/>
123 *     HttpClient client = new HttpClient();
124 *     client.getHostConfiguration().setHost("localhost", 443, authhttps);
125 *     // use relative url only
126 *     GetMethod httpget = new GetMethod("/");
127 *     client.executeMethod(httpget);
128 *     </pre>
129 * </p>
130 * <p/>
131 * Example of using custom protocol socket factory per default instead of the standard one:
132 * <pre>
133 *     Protocol authhttps = new Protocol("https",
134 *          new AuthSSLProtocolSocketFactory(
135 *              new URL("file:my.keystore"), "mypassword",
136 *              new URL("file:my.truststore"), "mypassword"), 443);
137 *     Protocol.registerProtocol("https", authhttps);
138 * <p/>
139 *     HttpClient client = new HttpClient();
140 *     GetMethod httpget = new GetMethod("https://localhost/");
141 *     client.executeMethod(httpget);
142 *     </pre>
143 * </p>
144 *
145 * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
146 *         <p/>
147 *         <p/>
148 *         DISCLAIMER: HttpClient developers DO NOT actively support this component.
149 *         The component is provided as a reference material, which may be inappropriate
150 *         for use without additional customization.
151 *         </p>
152 */
153
154public class AuthSSLProtocolSocketFactory extends HttpSecureProtocol {
155
156    /**
157     * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file
158     * must be given. Otherwise SSL context initialization error will result.
159     *
160     * @param keystoreUrl        URL of the keystore file. May be <tt>null</tt> if HTTPS client
161     *                           authentication is not to be used.
162     * @param keystorePassword   Password to unlock the keystore. IMPORTANT: this implementation
163     *                           assumes that the same password is used to protect the key and the keystore itself.
164     * @param truststoreUrl      URL of the truststore file. May be <tt>null</tt> if HTTPS server
165     *                           authentication is not to be used.
166     * @param truststorePassword Password to unlock the truststore.
167     */
168    public AuthSSLProtocolSocketFactory(final URL keystoreUrl,
169                                        final String keystorePassword,
170                                        final URL truststoreUrl,
171                                        final String truststorePassword)
172        throws GeneralSecurityException, IOException {
173
174        super();
175
176        // prepare key material
177        if (keystoreUrl != null) {
178            char[] ksPass = null;
179            if (keystorePassword != null) {
180                ksPass = keystorePassword.toCharArray();
181            }
182            KeyMaterial km = new KeyMaterial(keystoreUrl, ksPass);
183            super.setKeyMaterial(km);
184        }
185
186        // prepare trust material
187        if (truststoreUrl != null) {
188            char[] tsPass = null;
189            if (truststorePassword != null) {
190                tsPass = truststorePassword.toCharArray();
191            }
192            TrustMaterial tm;
193            try {
194                tm = new KeyMaterial(truststoreUrl, tsPass);
195            } catch (KeyStoreException kse) {
196                // KeyMaterial constructor blows up in no keys found,
197                // so we fall back to TrustMaterial constructor instead.
198                tm = new TrustMaterial(truststoreUrl, tsPass);
199            }
200            super.setTrustMaterial(tm);
201        }
202    }
203
204}