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@Deprecated 143public class SSLSocketFactory implements LayeredSocketFactory { 144 145 public static final String TLS = "TLS"; 146 public static final String SSL = "SSL"; 147 public static final String SSLV2 = "SSLv2"; 148 149 public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 150 = new AllowAllHostnameVerifier(); 151 152 public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 153 = new BrowserCompatHostnameVerifier(); 154 155 public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 156 = new StrictHostnameVerifier(); 157 158 /* 159 * Put defaults into holder class to avoid class preloading creating an 160 * instance of the classes referenced. 161 */ 162 private static class NoPreloadHolder { 163 /** 164 * The factory using the default JVM settings for secure connections. 165 */ 166 private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory(); 167 } 168 169 /** 170 * Gets an singleton instance of the SSLProtocolSocketFactory. 171 * @return a SSLProtocolSocketFactory 172 */ 173 public static SSLSocketFactory getSocketFactory() { 174 return NoPreloadHolder.DEFAULT_FACTORY; 175 } 176 177 private final SSLContext sslcontext; 178 private final javax.net.ssl.SSLSocketFactory socketfactory; 179 private final HostNameResolver nameResolver; 180 private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; 181 182 public SSLSocketFactory( 183 String algorithm, 184 final KeyStore keystore, 185 final String keystorePassword, 186 final KeyStore truststore, 187 final SecureRandom random, 188 final HostNameResolver nameResolver) 189 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 190 { 191 super(); 192 if (algorithm == null) { 193 algorithm = TLS; 194 } 195 KeyManager[] keymanagers = null; 196 if (keystore != null) { 197 keymanagers = createKeyManagers(keystore, keystorePassword); 198 } 199 TrustManager[] trustmanagers = null; 200 if (truststore != null) { 201 trustmanagers = createTrustManagers(truststore); 202 } 203 this.sslcontext = SSLContext.getInstance(algorithm); 204 this.sslcontext.init(keymanagers, trustmanagers, random); 205 this.socketfactory = this.sslcontext.getSocketFactory(); 206 this.nameResolver = nameResolver; 207 } 208 209 public SSLSocketFactory( 210 final KeyStore keystore, 211 final String keystorePassword, 212 final KeyStore truststore) 213 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 214 { 215 this(TLS, keystore, keystorePassword, truststore, null, null); 216 } 217 218 public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) 219 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 220 { 221 this(TLS, keystore, keystorePassword, null, null, null); 222 } 223 224 public SSLSocketFactory(final KeyStore truststore) 225 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException 226 { 227 this(TLS, null, null, truststore, null, null); 228 } 229 230 /** 231 * Constructs an HttpClient SSLSocketFactory backed by the given JSSE 232 * SSLSocketFactory. 233 * 234 * @hide 235 */ 236 public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) { 237 super(); 238 this.sslcontext = null; 239 this.socketfactory = socketfactory; 240 this.nameResolver = null; 241 } 242 243 /** 244 * Creates the default SSL socket factory. 245 * This constructor is used exclusively to instantiate the factory for 246 * {@link #getSocketFactory getSocketFactory}. 247 */ 248 private SSLSocketFactory() { 249 super(); 250 this.sslcontext = null; 251 this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 252 this.nameResolver = null; 253 } 254 255 private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password) 256 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 257 if (keystore == null) { 258 throw new IllegalArgumentException("Keystore may not be null"); 259 } 260 KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( 261 KeyManagerFactory.getDefaultAlgorithm()); 262 kmfactory.init(keystore, password != null ? password.toCharArray(): null); 263 return kmfactory.getKeyManagers(); 264 } 265 266 private static TrustManager[] createTrustManagers(final KeyStore keystore) 267 throws KeyStoreException, NoSuchAlgorithmException { 268 if (keystore == null) { 269 throw new IllegalArgumentException("Keystore may not be null"); 270 } 271 TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( 272 TrustManagerFactory.getDefaultAlgorithm()); 273 tmfactory.init(keystore); 274 return tmfactory.getTrustManagers(); 275 } 276 277 278 // non-javadoc, see interface org.apache.http.conn.SocketFactory 279 public Socket createSocket() 280 throws IOException { 281 282 // the cast makes sure that the factory is working as expected 283 return (SSLSocket) this.socketfactory.createSocket(); 284 } 285 286 287 // non-javadoc, see interface org.apache.http.conn.SocketFactory 288 public Socket connectSocket( 289 final Socket sock, 290 final String host, 291 final int port, 292 final InetAddress localAddress, 293 int localPort, 294 final HttpParams params 295 ) throws IOException { 296 297 if (host == null) { 298 throw new IllegalArgumentException("Target host may not be null."); 299 } 300 if (params == null) { 301 throw new IllegalArgumentException("Parameters may not be null."); 302 } 303 304 SSLSocket sslsock = (SSLSocket) 305 ((sock != null) ? sock : createSocket()); 306 307 if ((localAddress != null) || (localPort > 0)) { 308 309 // we need to bind explicitly 310 if (localPort < 0) 311 localPort = 0; // indicates "any" 312 313 InetSocketAddress isa = 314 new InetSocketAddress(localAddress, localPort); 315 sslsock.bind(isa); 316 } 317 318 int connTimeout = HttpConnectionParams.getConnectionTimeout(params); 319 int soTimeout = HttpConnectionParams.getSoTimeout(params); 320 321 InetSocketAddress remoteAddress; 322 if (this.nameResolver != null) { 323 remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); 324 } else { 325 remoteAddress = new InetSocketAddress(host, port); 326 } 327 328 sslsock.connect(remoteAddress, connTimeout); 329 330 sslsock.setSoTimeout(soTimeout); 331 try { 332 // BEGIN android-added 333 /* 334 * Make sure we have started the handshake before verifying. 335 * Otherwise when we go to the hostname verifier, it directly calls 336 * SSLSocket#getSession() which swallows SSL handshake errors. 337 */ 338 sslsock.startHandshake(); 339 // END android-added 340 hostnameVerifier.verify(host, sslsock); 341 // verifyHostName() didn't blowup - good! 342 } catch (IOException iox) { 343 // close the socket before re-throwing the exception 344 try { sslsock.close(); } catch (Exception x) { /*ignore*/ } 345 throw iox; 346 } 347 348 return sslsock; 349 } 350 351 352 /** 353 * Checks whether a socket connection is secure. 354 * This factory creates TLS/SSL socket connections 355 * which, by default, are considered secure. 356 * <br/> 357 * Derived classes may override this method to perform 358 * runtime checks, for example based on the cypher suite. 359 * 360 * @param sock the connected socket 361 * 362 * @return <code>true</code> 363 * 364 * @throws IllegalArgumentException if the argument is invalid 365 */ 366 public boolean isSecure(Socket sock) 367 throws IllegalArgumentException { 368 369 if (sock == null) { 370 throw new IllegalArgumentException("Socket may not be null."); 371 } 372 // This instanceof check is in line with createSocket() above. 373 if (!(sock instanceof SSLSocket)) { 374 throw new IllegalArgumentException 375 ("Socket not created by this factory."); 376 } 377 // This check is performed last since it calls the argument object. 378 if (sock.isClosed()) { 379 throw new IllegalArgumentException("Socket is closed."); 380 } 381 382 return true; 383 384 } // isSecure 385 386 387 // non-javadoc, see interface LayeredSocketFactory 388 public Socket createSocket( 389 final Socket socket, 390 final String host, 391 final int port, 392 final boolean autoClose 393 ) throws IOException, UnknownHostException { 394 SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket( 395 socket, 396 host, 397 port, 398 autoClose 399 ); 400 // BEGIN android-added 401 /* 402 * Make sure we have started the handshake before verifying. 403 * Otherwise when we go to the hostname verifier, it directly calls 404 * SSLSocket#getSession() which swallows SSL handshake errors. 405 */ 406 sslSocket.startHandshake(); 407 // END android-added 408 hostnameVerifier.verify(host, sslSocket); 409 // verifyHostName() didn't blowup - good! 410 return sslSocket; 411 } 412 413 public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { 414 if ( hostnameVerifier == null ) { 415 throw new IllegalArgumentException("Hostname verifier may not be null"); 416 } 417 this.hostnameVerifier = hostnameVerifier; 418 } 419 420 public X509HostnameVerifier getHostnameVerifier() { 421 return hostnameVerifier; 422 } 423 424} 425