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