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