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
32package org.apache.http.conn.ssl;
33
34import org.apache.http.conn.scheme.HostNameResolver;
35import org.apache.http.conn.scheme.LayeredSocketFactory;
36import org.apache.http.params.HttpConnectionParams;
37import org.apache.http.params.HttpParams;
38
39import javax.net.ssl.HttpsURLConnection;
40import javax.net.ssl.KeyManager;
41import javax.net.ssl.KeyManagerFactory;
42import javax.net.ssl.SSLContext;
43import javax.net.ssl.SSLSocket;
44import javax.net.ssl.TrustManager;
45import javax.net.ssl.TrustManagerFactory;
46import java.io.IOException;
47import java.net.InetAddress;
48import java.net.InetSocketAddress;
49import java.net.Socket;
50import java.net.UnknownHostException;
51import java.security.KeyManagementException;
52import java.security.KeyStore;
53import java.security.KeyStoreException;
54import java.security.NoSuchAlgorithmException;
55import java.security.SecureRandom;
56import 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
144public 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