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