mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /*
     2  * ====================================================================
     3  * Licensed to the Apache Software Foundation (ASF) under one
     4  * or more contributor license agreements.  See the NOTICE file
     5  * distributed with this work for additional information
     6  * regarding copyright ownership.  The ASF licenses this file
     7  * to you under the Apache License, Version 2.0 (the
     8  * "License"); you may not use this file except in compliance
     9  * with the License.  You may obtain a copy of the License at
    10  *
    11  *   http://www.apache.org/licenses/LICENSE-2.0
    12  *
    13  * Unless required by applicable law or agreed to in writing,
    14  * software distributed under the License is distributed on an
    15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    16  * KIND, either express or implied.  See the License for the
    17  * specific language governing permissions and limitations
    18  * under the License.
    19  * ====================================================================
    20  *
    21  * This software consists of voluntary contributions made by many
    22  * individuals on behalf of the Apache Software Foundation.  For more
    23  * information on the Apache Software Foundation, please see
    24  * <http://www.apache.org/>.
    25  *
    26  */
    28 package ch.boye.httpclientandroidlib.conn.ssl;
    30 import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
    32 import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException;
    33 import ch.boye.httpclientandroidlib.conn.scheme.HostNameResolver;
    34 import ch.boye.httpclientandroidlib.conn.scheme.LayeredSchemeSocketFactory;
    35 import ch.boye.httpclientandroidlib.conn.scheme.LayeredSocketFactory;
    36 import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
    37 import ch.boye.httpclientandroidlib.params.HttpParams;
    39 import javax.net.ssl.KeyManager;
    40 import javax.net.ssl.KeyManagerFactory;
    41 import javax.net.ssl.SSLContext;
    42 import javax.net.ssl.SSLSocket;
    43 import javax.net.ssl.TrustManager;
    44 import javax.net.ssl.TrustManagerFactory;
    45 import javax.net.ssl.X509TrustManager;
    47 import java.io.IOException;
    48 import java.net.InetAddress;
    49 import java.net.InetSocketAddress;
    50 import java.net.Socket;
    51 import java.net.SocketTimeoutException;
    52 import java.net.UnknownHostException;
    53 import java.security.KeyManagementException;
    54 import java.security.KeyStore;
    55 import java.security.KeyStoreException;
    56 import java.security.NoSuchAlgorithmException;
    57 import java.security.SecureRandom;
    58 import java.security.UnrecoverableKeyException;
    60 /**
    61  * Layered socket factory for TLS/SSL connections.
    62  * <p>
    63  * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of
    64  * trusted certificates and to authenticate to the HTTPS server using a private key.
    65  * <p>
    66  * SSLSocketFactory will enable server authentication when supplied with
    67  * a {@link KeyStore trust-store} file containing one or several trusted certificates. The client
    68  * secure socket will reject the connection during the SSL session handshake if the target HTTPS
    69  * server attempts to authenticate itself with a non-trusted certificate.
    70  * <p>
    71  * Use JDK keytool utility to import a trusted certificate and generate a trust-store file:
    72  *    <pre>
    73  *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
    74  *    </pre>
    75  * <p>
    76  * In special cases the standard trust verification process can be bypassed by using a custom
    77  * {@link TrustStrategy}. This interface is primarily intended for allowing self-signed
    78  * certificates to be accepted as trusted without having to add them to the trust-store file.
    79  * <p>
    80  * The following parameters can be used to customize the behavior of this
    81  * class:
    82  * <ul>
    83  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
    84  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li>
    85  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li>
    86  * </ul>
    87  * <p>
    88  * SSLSocketFactory will enable client authentication when supplied with
    89  * a {@link KeyStore key-store} file containing a private key/public certificate
    90  * pair. The client secure socket will use the private key to authenticate
    91  * itself to the target HTTPS server during the SSL session handshake if
    92  * requested to do so by the server.
    93  * The target HTTPS server will in its turn verify the certificate presented
    94  * by the client in order to establish client's authenticity
    95  * <p>
    96  * Use the following sequence of actions to generate a key-store file
    97  * </p>
    98  *   <ul>
    99  *     <li>
   100  *      <p>
   101  *      Use JDK keytool utility to generate a new key
   102  *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
   103  *      For simplicity use the same password for the key as that of the key-store
   104  *      </p>
   105  *     </li>
   106  *     <li>
   107  *      <p>
   108  *      Issue a certificate signing request (CSR)
   109  *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
   110  *     </p>
   111  *     </li>
   112  *     <li>
   113  *      <p>
   114  *      Send the certificate request to the trusted Certificate Authority for signature.
   115  *      One may choose to act as her own CA and sign the certificate request using a PKI
   116  *      tool, such as OpenSSL.
   117  *      </p>
   118  *     </li>
   119  *     <li>
   120  *      <p>
   121  *       Import the trusted CA root certificate
   122  *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
   123  *      </p>
   124  *     </li>
   125  *     <li>
   126  *      <p>
   127  *       Import the PKCS#7 file containg the complete certificate chain
   128  *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
   129  *      </p>
   130  *     </li>
   131  *     <li>
   132  *      <p>
   133  *       Verify the content the resultant keystore file
   134  *       <pre>keytool -list -v -keystore my.keystore</pre>
   135  *      </p>
   136  *     </li>
   137  *   </ul>
   138  *
   139  * @since 4.0
   140  */
   141 @SuppressWarnings("deprecation")
   142 @ThreadSafe
   143 public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSocketFactory {
   145     public static final String TLS   = "TLS";
   146     public static final String SSL   = "SSL";
   147     public static final String SSLV2 = "SSLv2";
   149     public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
   150         = new AllowAllHostnameVerifier();
   152     public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
   153         = new BrowserCompatHostnameVerifier();
   155     public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
   156         = new StrictHostnameVerifier();
   158     /**
   159      * Gets the default factory, which uses the default JVM settings for secure
   160      * connections.
   161      *
   162      * @return the default factory
   163      */
   164     public static SSLSocketFactory getSocketFactory() {
   165         return new SSLSocketFactory();
   166     }
   168     private final javax.net.ssl.SSLSocketFactory socketfactory;
   169     private final HostNameResolver nameResolver;
   170     // TODO: make final
   171     private volatile X509HostnameVerifier hostnameVerifier;
   173     private static SSLContext createSSLContext(
   174             String algorithm,
   175             final KeyStore keystore,
   176             final String keystorePassword,
   177             final KeyStore truststore,
   178             final SecureRandom random,
   179             final TrustStrategy trustStrategy)
   180                 throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
   181         if (algorithm == null) {
   182             algorithm = TLS;
   183         }
   184         KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
   185                 KeyManagerFactory.getDefaultAlgorithm());
   186         kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null);
   187         KeyManager[] keymanagers =  kmfactory.getKeyManagers();
   188         TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
   189                 TrustManagerFactory.getDefaultAlgorithm());
   190         tmfactory.init(truststore);
   191         TrustManager[] trustmanagers = tmfactory.getTrustManagers();
   192         if (trustmanagers != null && trustStrategy != null) {
   193             for (int i = 0; i < trustmanagers.length; i++) {
   194                 TrustManager tm = trustmanagers[i];
   195                 if (tm instanceof X509TrustManager) {
   196                     trustmanagers[i] = new TrustManagerDecorator(
   197                             (X509TrustManager) tm, trustStrategy);
   198                 }
   199             }
   200         }
   202         SSLContext sslcontext = SSLContext.getInstance(algorithm);
   203         sslcontext.init(keymanagers, trustmanagers, random);
   204         return sslcontext;
   205     }
   207     private static SSLContext createDefaultSSLContext() {
   208         try {
   209             return createSSLContext(TLS, null, null, null, null, null);
   210         } catch (Exception ex) {
   211             throw new IllegalStateException("Failure initializing default SSL context", ex);
   212         }
   213     }
   215     /**
   216      * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)}
   217      */
   218     @Deprecated
   219     public SSLSocketFactory(
   220             final String algorithm,
   221             final KeyStore keystore,
   222             final String keystorePassword,
   223             final KeyStore truststore,
   224             final SecureRandom random,
   225             final HostNameResolver nameResolver)
   226                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   227         this(createSSLContext(
   228                 algorithm, keystore, keystorePassword, truststore, random, null),
   229                 nameResolver);
   230     }
   232     /**
   233      * @since 4.1
   234      */
   235     public SSLSocketFactory(
   236             String algorithm,
   237             final KeyStore keystore,
   238             final String keystorePassword,
   239             final KeyStore truststore,
   240             final SecureRandom random,
   241             final X509HostnameVerifier hostnameVerifier)
   242                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   243         this(createSSLContext(
   244                 algorithm, keystore, keystorePassword, truststore, random, null),
   245                 hostnameVerifier);
   246     }
   248     /**
   249      * @since 4.1
   250      */
   251     public SSLSocketFactory(
   252             String algorithm,
   253             final KeyStore keystore,
   254             final String keystorePassword,
   255             final KeyStore truststore,
   256             final SecureRandom random,
   257             final TrustStrategy trustStrategy,
   258             final X509HostnameVerifier hostnameVerifier)
   259                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   260         this(createSSLContext(
   261                 algorithm, keystore, keystorePassword, truststore, random, trustStrategy),
   262                 hostnameVerifier);
   263     }
   265     public SSLSocketFactory(
   266             final KeyStore keystore,
   267             final String keystorePassword,
   268             final KeyStore truststore)
   269                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   270         this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
   271     }
   273     public SSLSocketFactory(
   274             final KeyStore keystore,
   275             final String keystorePassword)
   276                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{
   277         this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
   278     }
   280     public SSLSocketFactory(
   281             final KeyStore truststore)
   282                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   283         this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
   284     }
   286     /**
   287      * @since 4.1
   288      */
   289     public SSLSocketFactory(
   290             final TrustStrategy trustStrategy,
   291             final X509HostnameVerifier hostnameVerifier)
   292                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   293         this(TLS, null, null, null, null, trustStrategy, hostnameVerifier);
   294     }
   296     /**
   297      * @since 4.1
   298      */
   299     public SSLSocketFactory(
   300             final TrustStrategy trustStrategy)
   301                 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
   302         this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
   303     }
   305     public SSLSocketFactory(final SSLContext sslContext) {
   306         this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
   307     }
   309     /**
   310      * @deprecated Use {@link #SSLSocketFactory(SSLContext)}
   311      */
   312     @Deprecated
   313     public SSLSocketFactory(
   314             final SSLContext sslContext, final HostNameResolver nameResolver) {
   315         super();
   316         this.socketfactory = sslContext.getSocketFactory();
   317         this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
   318         this.nameResolver = nameResolver;
   319     }
   321     /**
   322      * @since 4.1
   323      */
   324     public SSLSocketFactory(
   325             final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
   326         super();
   327         this.socketfactory = sslContext.getSocketFactory();
   328         this.hostnameVerifier = hostnameVerifier;
   329         this.nameResolver = null;
   330     }
   332     private SSLSocketFactory() {
   333         this(createDefaultSSLContext());
   334     }
   336     /**
   337      * @param params Optional parameters. Parameters passed to this method will have no effect.
   338      *               This method will create a unconnected instance of {@link Socket} class.
   339      * @since 4.1
   340      */
   341     public Socket createSocket(final HttpParams params) throws IOException {
   342         return this.socketfactory.createSocket();
   343     }
   345     @Deprecated
   346     public Socket createSocket() throws IOException {
   347         return this.socketfactory.createSocket();
   348     }
   350     /**
   351      * @since 4.1
   352      */
   353     public Socket connectSocket(
   354             final Socket socket,
   355             final InetSocketAddress remoteAddress,
   356             final InetSocketAddress localAddress,
   357             final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
   358         if (remoteAddress == null) {
   359             throw new IllegalArgumentException("Remote address may not be null");
   360         }
   361         if (params == null) {
   362             throw new IllegalArgumentException("HTTP parameters may not be null");
   363         }
   364         Socket sock = socket != null ? socket : new Socket();
   365         if (localAddress != null) {
   366             sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
   367             sock.bind(localAddress);
   368         }
   370         int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
   371         int soTimeout = HttpConnectionParams.getSoTimeout(params);
   373         try {
   374             sock.setSoTimeout(soTimeout);
   375             sock.connect(remoteAddress, connTimeout);
   376         } catch (SocketTimeoutException ex) {
   377             throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
   378         }
   380         // HttpInetSocketAddress#toString() returns original hostname value of the remote address
   381         String hostname = remoteAddress.toString();
   382         int port = remoteAddress.getPort();
   383         String s = ":" + port;
   384         if (hostname.endsWith(s)) {
   385             hostname = hostname.substring(0, hostname.length() - s.length());
   386         }
   388         SSLSocket sslsock;
   389         // Setup SSL layering if necessary
   390         if (sock instanceof SSLSocket) {
   391             sslsock = (SSLSocket) sock;
   392         } else {
   393             sslsock = (SSLSocket) this.socketfactory.createSocket(sock, hostname, port, true);
   394         }
   395         if (this.hostnameVerifier != null) {
   396             try {
   397                 this.hostnameVerifier.verify(hostname, sslsock);
   398                 // verifyHostName() didn't blowup - good!
   399             } catch (IOException iox) {
   400                 // close the socket before re-throwing the exception
   401                 try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
   402                 throw iox;
   403             }
   404         }
   405         return sslsock;
   406     }
   409     /**
   410      * Checks whether a socket connection is secure.
   411      * This factory creates TLS/SSL socket connections
   412      * which, by default, are considered secure.
   413      * <br/>
   414      * Derived classes may override this method to perform
   415      * runtime checks, for example based on the cypher suite.
   416      *
   417      * @param sock      the connected socket
   418      *
   419      * @return  <code>true</code>
   420      *
   421      * @throws IllegalArgumentException if the argument is invalid
   422      */
   423     public boolean isSecure(final Socket sock) throws IllegalArgumentException {
   424         if (sock == null) {
   425             throw new IllegalArgumentException("Socket may not be null");
   426         }
   427         // This instanceof check is in line with createSocket() above.
   428         if (!(sock instanceof SSLSocket)) {
   429             throw new IllegalArgumentException("Socket not created by this factory");
   430         }
   431         // This check is performed last since it calls the argument object.
   432         if (sock.isClosed()) {
   433             throw new IllegalArgumentException("Socket is closed");
   434         }
   435         return true;
   436     }
   438     /**
   439      * @since 4.1
   440      */
   441     public Socket createLayeredSocket(
   442         final Socket socket,
   443         final String host,
   444         final int port,
   445         final boolean autoClose) throws IOException, UnknownHostException {
   446         SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
   447               socket,
   448               host,
   449               port,
   450               autoClose
   451         );
   452         if (this.hostnameVerifier != null) {
   453             this.hostnameVerifier.verify(host, sslSocket);
   454         }
   455         // verifyHostName() didn't blowup - good!
   456         return sslSocket;
   457     }
   459     @Deprecated
   460     public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
   461         if ( hostnameVerifier == null ) {
   462             throw new IllegalArgumentException("Hostname verifier may not be null");
   463         }
   464         this.hostnameVerifier = hostnameVerifier;
   465     }
   467     public X509HostnameVerifier getHostnameVerifier() {
   468         return this.hostnameVerifier;
   469     }
   471     /**
   472      * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
   473      */
   474     @Deprecated
   475     public Socket connectSocket(
   476             final Socket socket,
   477             final String host, int port,
   478             final InetAddress localAddress, int localPort,
   479             final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
   480         InetSocketAddress local = null;
   481         if (localAddress != null || localPort > 0) {
   482             // we need to bind explicitly
   483             if (localPort < 0) {
   484                 localPort = 0; // indicates "any"
   485             }
   486             local = new InetSocketAddress(localAddress, localPort);
   487         }
   488         InetAddress remoteAddress;
   489         if (this.nameResolver != null) {
   490             remoteAddress = this.nameResolver.resolve(host);
   491         } else {
   492             remoteAddress = InetAddress.getByName(host);
   493         }
   494         InetSocketAddress remote = new InetSocketAddress(remoteAddress, port);
   495         return connectSocket(socket, remote, local, params);
   496     }
   498     /**
   499      * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)}
   500      */
   501     @Deprecated
   502     public Socket createSocket(
   503             final Socket socket,
   504             final String host, int port,
   505             boolean autoClose) throws IOException, UnknownHostException {
   506         return createLayeredSocket(socket, host, port, autoClose);
   507     }
   509 }

mercurial