    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}