Home | History | Annotate | Download | only in ssl
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
      3  * $Revision: 659194 $
      4  * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
      5  *
      6  * ====================================================================
      7  * Licensed to the Apache Software Foundation (ASF) under one
      8  * or more contributor license agreements.  See the NOTICE file
      9  * distributed with this work for additional information
     10  * regarding copyright ownership.  The ASF licenses this file
     11  * to you under the Apache License, Version 2.0 (the
     12  * "License"); you may not use this file except in compliance
     13  * with the License.  You may obtain a copy of the License at
     14  *
     15  *   http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing,
     18  * software distributed under the License is distributed on an
     19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     20  * KIND, either express or implied.  See the License for the
     21  * specific language governing permissions and limitations
     22  * under the License.
     23  * ====================================================================
     24  *
     25  * This software consists of voluntary contributions made by many
     26  * individuals on behalf of the Apache Software Foundation.  For more
     27  * information on the Apache Software Foundation, please see
     28  * <http://www.apache.org/>.
     29  *
     30  */
     31 
     32 package org.apache.http.conn.ssl;
     33 
     34 import org.apache.http.conn.scheme.HostNameResolver;
     35 import org.apache.http.conn.scheme.LayeredSocketFactory;
     36 import org.apache.http.params.HttpConnectionParams;
     37 import org.apache.http.params.HttpParams;
     38 
     39 import javax.net.ssl.HttpsURLConnection;
     40 import javax.net.ssl.KeyManager;
     41 import javax.net.ssl.KeyManagerFactory;
     42 import javax.net.ssl.SSLContext;
     43 import javax.net.ssl.SSLSocket;
     44 import javax.net.ssl.TrustManager;
     45 import javax.net.ssl.TrustManagerFactory;
     46 import java.io.IOException;
     47 import java.net.InetAddress;
     48 import java.net.InetSocketAddress;
     49 import java.net.Socket;
     50 import java.net.UnknownHostException;
     51 import java.security.KeyManagementException;
     52 import java.security.KeyStore;
     53 import java.security.KeyStoreException;
     54 import java.security.NoSuchAlgorithmException;
     55 import java.security.SecureRandom;
     56 import java.security.UnrecoverableKeyException;
     57 
     58 /**
     59  * Layered socket factory for TLS/SSL connections, based on JSSE.
     60  *.
     61  * <p>
     62  * SSLSocketFactory can be used to validate the identity of the HTTPS
     63  * server against a list of trusted certificates and to authenticate to
     64  * the HTTPS server using a private key.
     65  * </p>
     66  *
     67  * <p>
     68  * SSLSocketFactory will enable server authentication when supplied with
     69  * a {@link KeyStore truststore} file containg one or several trusted
     70  * certificates. The client secure socket will reject the connection during
     71  * the SSL session handshake if the target HTTPS server attempts to
     72  * authenticate itself with a non-trusted certificate.
     73  * </p>
     74  *
     75  * <p>
     76  * Use JDK keytool utility to import a trusted certificate and generate a truststore file:
     77  *    <pre>
     78  *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
     79  *    </pre>
     80  * </p>
     81  *
     82  * <p>
     83  * SSLSocketFactory will enable client authentication when supplied with
     84  * a {@link KeyStore keystore} file containg a private key/public certificate
     85  * pair. The client secure socket will use the private key to authenticate
     86  * itself to the target HTTPS server during the SSL session handshake if
     87  * requested to do so by the server.
     88  * The target HTTPS server will in its turn verify the certificate presented
     89  * by the client in order to establish client's authenticity
     90  * </p>
     91  *
     92  * <p>
     93  * Use the following sequence of actions to generate a keystore file
     94  * </p>
     95  *   <ul>
     96  *     <li>
     97  *      <p>
     98  *      Use JDK keytool utility to generate a new key
     99  *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
    100  *      For simplicity use the same password for the key as that of the keystore
    101  *      </p>
    102  *     </li>
    103  *     <li>
    104  *      <p>
    105  *      Issue a certificate signing request (CSR)
    106  *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
    107  *     </p>
    108  *     </li>
    109  *     <li>
    110  *      <p>
    111  *      Send the certificate request to the trusted Certificate Authority for signature.
    112  *      One may choose to act as her own CA and sign the certificate request using a PKI
    113  *      tool, such as OpenSSL.
    114  *      </p>
    115  *     </li>
    116  *     <li>
    117  *      <p>
    118  *       Import the trusted CA root certificate
    119  *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
    120  *      </p>
    121  *     </li>
    122  *     <li>
    123  *      <p>
    124  *       Import the PKCS#7 file containg the complete certificate chain
    125  *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
    126  *      </p>
    127  *     </li>
    128  *     <li>
    129  *      <p>
    130  *       Verify the content the resultant keystore file
    131  *       <pre>keytool -list -v -keystore my.keystore</pre>
    132  *      </p>
    133  *     </li>
    134  *   </ul>
    135  * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
    136  * @author Julius Davies
    137  *
    138  * @deprecated Please use {@link java.net.URL#openConnection} instead.
    139  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
    140  *     for further details.
    141  */
    142 
    143 @Deprecated
    144 public class SSLSocketFactory implements LayeredSocketFactory {
    145 
    146     public static final String TLS   = "TLS";
    147     public static final String SSL   = "SSL";
    148     public static final String SSLV2 = "SSLv2";
    149 
    150     public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
    151         = new AllowAllHostnameVerifier();
    152 
    153     public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
    154         = new BrowserCompatHostnameVerifier();
    155 
    156     public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
    157         = new StrictHostnameVerifier();
    158 
    159     /*
    160      * Put defaults into holder class to avoid class preloading creating an
    161      * instance of the classes referenced.
    162      */
    163     private static class NoPreloadHolder {
    164         /**
    165          * The factory using the default JVM settings for secure connections.
    166          */
    167         private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
    168     }
    169 
    170     /**
    171      * Gets an singleton instance of the SSLProtocolSocketFactory.
    172      * @return a SSLProtocolSocketFactory
    173      */
    174     public static SSLSocketFactory getSocketFactory() {
    175         return NoPreloadHolder.DEFAULT_FACTORY;
    176     }
    177 
    178     private final SSLContext sslcontext;
    179     private final javax.net.ssl.SSLSocketFactory socketfactory;
    180     private final HostNameResolver nameResolver;
    181     private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
    182 
    183     public SSLSocketFactory(
    184         String algorithm,
    185         final KeyStore keystore,
    186         final String keystorePassword,
    187         final KeyStore truststore,
    188         final SecureRandom random,
    189         final HostNameResolver nameResolver)
    190         throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
    191     {
    192         super();
    193         if (algorithm == null) {
    194             algorithm = TLS;
    195         }
    196         KeyManager[] keymanagers = null;
    197         if (keystore != null) {
    198             keymanagers = createKeyManagers(keystore, keystorePassword);
    199         }
    200         TrustManager[] trustmanagers = null;
    201         if (truststore != null) {
    202             trustmanagers = createTrustManagers(truststore);
    203         }
    204         this.sslcontext = SSLContext.getInstance(algorithm);
    205         this.sslcontext.init(keymanagers, trustmanagers, random);
    206         this.socketfactory = this.sslcontext.getSocketFactory();
    207         this.nameResolver = nameResolver;
    208     }
    209 
    210     public SSLSocketFactory(
    211             final KeyStore keystore,
    212             final String keystorePassword,
    213             final KeyStore truststore)
    214             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
    215     {
    216         this(TLS, keystore, keystorePassword, truststore, null, null);
    217     }
    218 
    219     public SSLSocketFactory(final KeyStore keystore, final String keystorePassword)
    220             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
    221     {
    222         this(TLS, keystore, keystorePassword, null, null, null);
    223     }
    224 
    225     public SSLSocketFactory(final KeyStore truststore)
    226             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
    227     {
    228         this(TLS, null, null, truststore, null, null);
    229     }
    230 
    231     /**
    232      * Constructs an HttpClient SSLSocketFactory backed by the given JSSE
    233      * SSLSocketFactory.
    234      *
    235      * @hide
    236      */
    237     public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) {
    238         super();
    239         this.sslcontext = null;
    240         this.socketfactory = socketfactory;
    241         this.nameResolver = null;
    242     }
    243 
    244     /**
    245      * Creates the default SSL socket factory.
    246      * This constructor is used exclusively to instantiate the factory for
    247      * {@link #getSocketFactory getSocketFactory}.
    248      */
    249     private SSLSocketFactory() {
    250         super();
    251         this.sslcontext = null;
    252         this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
    253         this.nameResolver = null;
    254     }
    255 
    256     private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
    257         throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
    258         if (keystore == null) {
    259             throw new IllegalArgumentException("Keystore may not be null");
    260         }
    261         KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
    262             KeyManagerFactory.getDefaultAlgorithm());
    263         kmfactory.init(keystore, password != null ? password.toCharArray(): null);
    264         return kmfactory.getKeyManagers();
    265     }
    266 
    267     private static TrustManager[] createTrustManagers(final KeyStore keystore)
    268         throws KeyStoreException, NoSuchAlgorithmException {
    269         if (keystore == null) {
    270             throw new IllegalArgumentException("Keystore may not be null");
    271         }
    272         TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
    273             TrustManagerFactory.getDefaultAlgorithm());
    274         tmfactory.init(keystore);
    275         return tmfactory.getTrustManagers();
    276     }
    277 
    278 
    279     // non-javadoc, see interface org.apache.http.conn.SocketFactory
    280     public Socket createSocket()
    281         throws IOException {
    282 
    283         // the cast makes sure that the factory is working as expected
    284         return (SSLSocket) this.socketfactory.createSocket();
    285     }
    286 
    287 
    288     // non-javadoc, see interface org.apache.http.conn.SocketFactory
    289     public Socket connectSocket(
    290         final Socket sock,
    291         final String host,
    292         final int port,
    293         final InetAddress localAddress,
    294         int localPort,
    295         final HttpParams params
    296     ) throws IOException {
    297 
    298         if (host == null) {
    299             throw new IllegalArgumentException("Target host may not be null.");
    300         }
    301         if (params == null) {
    302             throw new IllegalArgumentException("Parameters may not be null.");
    303         }
    304 
    305         SSLSocket sslsock = (SSLSocket)
    306             ((sock != null) ? sock : createSocket());
    307 
    308         if ((localAddress != null) || (localPort > 0)) {
    309 
    310             // we need to bind explicitly
    311             if (localPort < 0)
    312                 localPort = 0; // indicates "any"
    313 
    314             InetSocketAddress isa =
    315                 new InetSocketAddress(localAddress, localPort);
    316             sslsock.bind(isa);
    317         }
    318 
    319         int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
    320         int soTimeout = HttpConnectionParams.getSoTimeout(params);
    321 
    322         InetSocketAddress remoteAddress;
    323         if (this.nameResolver != null) {
    324             remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port);
    325         } else {
    326             remoteAddress = new InetSocketAddress(host, port);
    327         }
    328 
    329         sslsock.connect(remoteAddress, connTimeout);
    330 
    331         sslsock.setSoTimeout(soTimeout);
    332         try {
    333             hostnameVerifier.verify(host, sslsock);
    334             // verifyHostName() didn't blowup - good!
    335         } catch (IOException iox) {
    336             // close the socket before re-throwing the exception
    337             try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
    338             throw iox;
    339         }
    340 
    341         return sslsock;
    342     }
    343 
    344 
    345     /**
    346      * Checks whether a socket connection is secure.
    347      * This factory creates TLS/SSL socket connections
    348      * which, by default, are considered secure.
    349      * <br/>
    350      * Derived classes may override this method to perform
    351      * runtime checks, for example based on the cypher suite.
    352      *
    353      * @param sock      the connected socket
    354      *
    355      * @return  <code>true</code>
    356      *
    357      * @throws IllegalArgumentException if the argument is invalid
    358      */
    359     public boolean isSecure(Socket sock)
    360         throws IllegalArgumentException {
    361 
    362         if (sock == null) {
    363             throw new IllegalArgumentException("Socket may not be null.");
    364         }
    365         // This instanceof check is in line with createSocket() above.
    366         if (!(sock instanceof SSLSocket)) {
    367             throw new IllegalArgumentException
    368                 ("Socket not created by this factory.");
    369         }
    370         // This check is performed last since it calls the argument object.
    371         if (sock.isClosed()) {
    372             throw new IllegalArgumentException("Socket is closed.");
    373         }
    374 
    375         return true;
    376 
    377     } // isSecure
    378 
    379 
    380     // non-javadoc, see interface LayeredSocketFactory
    381     public Socket createSocket(
    382         final Socket socket,
    383         final String host,
    384         final int port,
    385         final boolean autoClose
    386     ) throws IOException, UnknownHostException {
    387         SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
    388               socket,
    389               host,
    390               port,
    391               autoClose
    392         );
    393         hostnameVerifier.verify(host, sslSocket);
    394         // verifyHostName() didn't blowup - good!
    395         return sslSocket;
    396     }
    397 
    398     public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
    399         if ( hostnameVerifier == null ) {
    400             throw new IllegalArgumentException("Hostname verifier may not be null");
    401         }
    402         this.hostnameVerifier = hostnameVerifier;
    403     }
    404 
    405     public X509HostnameVerifier getHostnameVerifier() {
    406         return hostnameVerifier;
    407     }
    408 
    409 }
    410