001/*
002 * $Header$
003 * $Revision: 129 $
004 * $Date: 2007-11-14 19:21:33 -0800 (Wed, 14 Nov 2007) $
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;
039
040/**
041 * <p/>
042 * AuthSSLProtocolSocketFactory can be used to validate the identity of the HTTPS
043 * server against a list of trusted certificates and to authenticate to the HTTPS
044 * server using a private key.
045 * </p>
046 * <p/>
047 * <p/>
048 * AuthSSLProtocolSocketFactory will enable server authentication when supplied with
049 * a {@link java.security.KeyStore truststore} file containg one or several trusted certificates.
050 * The client secure socket will reject the connection during the SSL session handshake
051 * if the target HTTPS server attempts to authenticate itself with a non-trusted
052 * certificate.
053 * </p>
054 * <p/>
055 * <p/>
056 * Use JDK keytool utility to import a trusted certificate and generate a truststore file:
057 * <pre>
058 *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
059 *    </pre>
060 * </p>
061 * <p/>
062 * <p/>
063 * AuthSSLProtocolSocketFactory will enable client authentication when supplied with
064 * a {@link java.security.KeyStore keystore} file containg a private key/public certificate pair.
065 * The client secure socket will use the private key to authenticate itself to the target
066 * HTTPS server during the SSL session handshake if requested to do so by the server.
067 * The target HTTPS server will in its turn verify the certificate presented by the client
068 * in order to establish client's authenticity
069 * </p>
070 * <p/>
071 * <p/>
072 * Use the following sequence of actions to generate a keystore file
073 * </p>
074 * <ul>
075 * <li>
076 * <p/>
077 * Use JDK keytool utility to generate a new key
078 * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
079 * For simplicity use the same password for the key as that of the keystore
080 * </p>
081 * </li>
082 * <li>
083 * <p/>
084 * Issue a certificate signing request (CSR)
085 * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
086 * </p>
087 * </li>
088 * <li>
089 * <p/>
090 * Send the certificate request to the trusted Certificate Authority for signature.
091 * One may choose to act as her own CA and sign the certificate request using a PKI
092 * tool, such as OpenSSL.
093 * </p>
094 * </li>
095 * <li>
096 * <p/>
097 * Import the trusted CA root certificate
098 * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
099 * </p>
100 * </li>
101 * <li>
102 * <p/>
103 * Import the PKCS#7 file containg the complete certificate chain
104 * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
105 * </p>
106 * </li>
107 * <li>
108 * <p/>
109 * Verify the content the resultant keystore file
110 * <pre>keytool -list -v -keystore my.keystore</pre>
111 * </p>
112 * </li>
113 * </ul>
114 * <p/>
115 * Example of using custom protocol socket factory for a specific host:
116 * <pre>
117 *     Protocol authhttps = new Protocol("https",
118 *          new AuthSSLProtocolSocketFactory(
119 *              new URL("file:my.keystore"), "mypassword",
120 *              new URL("file:my.truststore"), "mypassword"), 443);
121 * <p/>
122 *     HttpClient client = new HttpClient();
123 *     client.getHostConfiguration().setHost("localhost", 443, authhttps);
124 *     // use relative url only
125 *     GetMethod httpget = new GetMethod("/");
126 *     client.executeMethod(httpget);
127 *     </pre>
128 * </p>
129 * <p/>
130 * Example of using custom protocol socket factory per default instead of the standard one:
131 * <pre>
132 *     Protocol authhttps = new Protocol("https",
133 *          new AuthSSLProtocolSocketFactory(
134 *              new URL("file:my.keystore"), "mypassword",
135 *              new URL("file:my.truststore"), "mypassword"), 443);
136 *     Protocol.registerProtocol("https", authhttps);
137 * <p/>
138 *     HttpClient client = new HttpClient();
139 *     GetMethod httpget = new GetMethod("https://localhost/");
140 *     client.executeMethod(httpget);
141 *     </pre>
142 * </p>
143 *
144 * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
145 *         <p/>
146 *         <p/>
147 *         DISCLAIMER: HttpClient developers DO NOT actively support this component.
148 *         The component is provided as a reference material, which may be inappropriate
149 *         for use without additional customization.
150 *         </p>
151 */
152
153public class AuthSSLProtocolSocketFactory extends HttpSecureProtocol {
154
155    /**
156     * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file
157     * must be given. Otherwise SSL context initialization error will result.
158     *
159     * @param keystoreUrl        URL of the keystore file. May be <tt>null</tt> if HTTPS client
160     *                           authentication is not to be used.
161     * @param keystorePassword   Password to unlock the keystore. IMPORTANT: this implementation
162     *                           assumes that the same password is used to protect the key and the keystore itself.
163     * @param truststoreUrl      URL of the truststore file. May be <tt>null</tt> if HTTPS server
164     *                           authentication is not to be used.
165     * @param truststorePassword Password to unlock the truststore.
166     */
167    public AuthSSLProtocolSocketFactory(final URL keystoreUrl,
168                                        final String keystorePassword,
169                                        final URL truststoreUrl,
170                                        final String truststorePassword)
171        throws GeneralSecurityException, IOException {
172
173        super();
174
175        // prepare key material
176        if (keystoreUrl != null) {
177            char[] ksPass = null;
178            if (keystorePassword != null) {
179                ksPass = keystorePassword.toCharArray();
180            }
181            KeyMaterial km = new KeyMaterial(keystoreUrl, ksPass);
182            super.setKeyMaterial(km);
183        }
184
185        // prepare trust material1
186        if (truststoreUrl != null) {
187            char[] tsPass = null;
188            if (truststorePassword != null) {
189                tsPass = truststorePassword.toCharArray();
190            }
191            TrustMaterial tm = new KeyMaterial(truststoreUrl, tsPass);
192            super.setTrustMaterial(tm);
193        }
194    }
195
196}