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