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