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