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    
030    package org.apache.commons.httpclient.contrib.ssl;
031    
032    import org.apache.commons.ssl.HttpSecureProtocol;
033    import org.apache.commons.ssl.KeyMaterial;
034    import org.apache.commons.ssl.TrustMaterial;
035    
036    import java.io.IOException;
037    import java.net.URL;
038    import 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    
153    public 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    }