1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,509 @@ 1.4 +/* 1.5 + * ==================================================================== 1.6 + * Licensed to the Apache Software Foundation (ASF) under one 1.7 + * or more contributor license agreements. See the NOTICE file 1.8 + * distributed with this work for additional information 1.9 + * regarding copyright ownership. The ASF licenses this file 1.10 + * to you under the Apache License, Version 2.0 (the 1.11 + * "License"); you may not use this file except in compliance 1.12 + * with the License. You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, 1.17 + * software distributed under the License is distributed on an 1.18 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1.19 + * KIND, either express or implied. See the License for the 1.20 + * specific language governing permissions and limitations 1.21 + * under the License. 1.22 + * ==================================================================== 1.23 + * 1.24 + * This software consists of voluntary contributions made by many 1.25 + * individuals on behalf of the Apache Software Foundation. For more 1.26 + * information on the Apache Software Foundation, please see 1.27 + * <http://www.apache.org/>. 1.28 + * 1.29 + */ 1.30 + 1.31 +package ch.boye.httpclientandroidlib.conn.ssl; 1.32 + 1.33 +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; 1.34 + 1.35 +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; 1.36 +import ch.boye.httpclientandroidlib.conn.scheme.HostNameResolver; 1.37 +import ch.boye.httpclientandroidlib.conn.scheme.LayeredSchemeSocketFactory; 1.38 +import ch.boye.httpclientandroidlib.conn.scheme.LayeredSocketFactory; 1.39 +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; 1.40 +import ch.boye.httpclientandroidlib.params.HttpParams; 1.41 + 1.42 +import javax.net.ssl.KeyManager; 1.43 +import javax.net.ssl.KeyManagerFactory; 1.44 +import javax.net.ssl.SSLContext; 1.45 +import javax.net.ssl.SSLSocket; 1.46 +import javax.net.ssl.TrustManager; 1.47 +import javax.net.ssl.TrustManagerFactory; 1.48 +import javax.net.ssl.X509TrustManager; 1.49 + 1.50 +import java.io.IOException; 1.51 +import java.net.InetAddress; 1.52 +import java.net.InetSocketAddress; 1.53 +import java.net.Socket; 1.54 +import java.net.SocketTimeoutException; 1.55 +import java.net.UnknownHostException; 1.56 +import java.security.KeyManagementException; 1.57 +import java.security.KeyStore; 1.58 +import java.security.KeyStoreException; 1.59 +import java.security.NoSuchAlgorithmException; 1.60 +import java.security.SecureRandom; 1.61 +import java.security.UnrecoverableKeyException; 1.62 + 1.63 +/** 1.64 + * Layered socket factory for TLS/SSL connections. 1.65 + * <p> 1.66 + * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of 1.67 + * trusted certificates and to authenticate to the HTTPS server using a private key. 1.68 + * <p> 1.69 + * SSLSocketFactory will enable server authentication when supplied with 1.70 + * a {@link KeyStore trust-store} file containing one or several trusted certificates. The client 1.71 + * secure socket will reject the connection during the SSL session handshake if the target HTTPS 1.72 + * server attempts to authenticate itself with a non-trusted certificate. 1.73 + * <p> 1.74 + * Use JDK keytool utility to import a trusted certificate and generate a trust-store file: 1.75 + * <pre> 1.76 + * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore 1.77 + * </pre> 1.78 + * <p> 1.79 + * In special cases the standard trust verification process can be bypassed by using a custom 1.80 + * {@link TrustStrategy}. This interface is primarily intended for allowing self-signed 1.81 + * certificates to be accepted as trusted without having to add them to the trust-store file. 1.82 + * <p> 1.83 + * The following parameters can be used to customize the behavior of this 1.84 + * class: 1.85 + * <ul> 1.86 + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li> 1.87 + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li> 1.88 + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li> 1.89 + * </ul> 1.90 + * <p> 1.91 + * SSLSocketFactory will enable client authentication when supplied with 1.92 + * a {@link KeyStore key-store} file containing a private key/public certificate 1.93 + * pair. The client secure socket will use the private key to authenticate 1.94 + * itself to the target HTTPS server during the SSL session handshake if 1.95 + * requested to do so by the server. 1.96 + * The target HTTPS server will in its turn verify the certificate presented 1.97 + * by the client in order to establish client's authenticity 1.98 + * <p> 1.99 + * Use the following sequence of actions to generate a key-store file 1.100 + * </p> 1.101 + * <ul> 1.102 + * <li> 1.103 + * <p> 1.104 + * Use JDK keytool utility to generate a new key 1.105 + * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> 1.106 + * For simplicity use the same password for the key as that of the key-store 1.107 + * </p> 1.108 + * </li> 1.109 + * <li> 1.110 + * <p> 1.111 + * Issue a certificate signing request (CSR) 1.112 + * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> 1.113 + * </p> 1.114 + * </li> 1.115 + * <li> 1.116 + * <p> 1.117 + * Send the certificate request to the trusted Certificate Authority for signature. 1.118 + * One may choose to act as her own CA and sign the certificate request using a PKI 1.119 + * tool, such as OpenSSL. 1.120 + * </p> 1.121 + * </li> 1.122 + * <li> 1.123 + * <p> 1.124 + * Import the trusted CA root certificate 1.125 + * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> 1.126 + * </p> 1.127 + * </li> 1.128 + * <li> 1.129 + * <p> 1.130 + * Import the PKCS#7 file containg the complete certificate chain 1.131 + * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> 1.132 + * </p> 1.133 + * </li> 1.134 + * <li> 1.135 + * <p> 1.136 + * Verify the content the resultant keystore file 1.137 + * <pre>keytool -list -v -keystore my.keystore</pre> 1.138 + * </p> 1.139 + * </li> 1.140 + * </ul> 1.141 + * 1.142 + * @since 4.0 1.143 + */ 1.144 +@SuppressWarnings("deprecation") 1.145 +@ThreadSafe 1.146 +public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSocketFactory { 1.147 + 1.148 + public static final String TLS = "TLS"; 1.149 + public static final String SSL = "SSL"; 1.150 + public static final String SSLV2 = "SSLv2"; 1.151 + 1.152 + public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 1.153 + = new AllowAllHostnameVerifier(); 1.154 + 1.155 + public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 1.156 + = new BrowserCompatHostnameVerifier(); 1.157 + 1.158 + public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 1.159 + = new StrictHostnameVerifier(); 1.160 + 1.161 + /** 1.162 + * Gets the default factory, which uses the default JVM settings for secure 1.163 + * connections. 1.164 + * 1.165 + * @return the default factory 1.166 + */ 1.167 + public static SSLSocketFactory getSocketFactory() { 1.168 + return new SSLSocketFactory(); 1.169 + } 1.170 + 1.171 + private final javax.net.ssl.SSLSocketFactory socketfactory; 1.172 + private final HostNameResolver nameResolver; 1.173 + // TODO: make final 1.174 + private volatile X509HostnameVerifier hostnameVerifier; 1.175 + 1.176 + private static SSLContext createSSLContext( 1.177 + String algorithm, 1.178 + final KeyStore keystore, 1.179 + final String keystorePassword, 1.180 + final KeyStore truststore, 1.181 + final SecureRandom random, 1.182 + final TrustStrategy trustStrategy) 1.183 + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException { 1.184 + if (algorithm == null) { 1.185 + algorithm = TLS; 1.186 + } 1.187 + KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( 1.188 + KeyManagerFactory.getDefaultAlgorithm()); 1.189 + kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null); 1.190 + KeyManager[] keymanagers = kmfactory.getKeyManagers(); 1.191 + TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( 1.192 + TrustManagerFactory.getDefaultAlgorithm()); 1.193 + tmfactory.init(truststore); 1.194 + TrustManager[] trustmanagers = tmfactory.getTrustManagers(); 1.195 + if (trustmanagers != null && trustStrategy != null) { 1.196 + for (int i = 0; i < trustmanagers.length; i++) { 1.197 + TrustManager tm = trustmanagers[i]; 1.198 + if (tm instanceof X509TrustManager) { 1.199 + trustmanagers[i] = new TrustManagerDecorator( 1.200 + (X509TrustManager) tm, trustStrategy); 1.201 + } 1.202 + } 1.203 + } 1.204 + 1.205 + SSLContext sslcontext = SSLContext.getInstance(algorithm); 1.206 + sslcontext.init(keymanagers, trustmanagers, random); 1.207 + return sslcontext; 1.208 + } 1.209 + 1.210 + private static SSLContext createDefaultSSLContext() { 1.211 + try { 1.212 + return createSSLContext(TLS, null, null, null, null, null); 1.213 + } catch (Exception ex) { 1.214 + throw new IllegalStateException("Failure initializing default SSL context", ex); 1.215 + } 1.216 + } 1.217 + 1.218 + /** 1.219 + * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)} 1.220 + */ 1.221 + @Deprecated 1.222 + public SSLSocketFactory( 1.223 + final String algorithm, 1.224 + final KeyStore keystore, 1.225 + final String keystorePassword, 1.226 + final KeyStore truststore, 1.227 + final SecureRandom random, 1.228 + final HostNameResolver nameResolver) 1.229 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.230 + this(createSSLContext( 1.231 + algorithm, keystore, keystorePassword, truststore, random, null), 1.232 + nameResolver); 1.233 + } 1.234 + 1.235 + /** 1.236 + * @since 4.1 1.237 + */ 1.238 + public SSLSocketFactory( 1.239 + String algorithm, 1.240 + final KeyStore keystore, 1.241 + final String keystorePassword, 1.242 + final KeyStore truststore, 1.243 + final SecureRandom random, 1.244 + final X509HostnameVerifier hostnameVerifier) 1.245 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.246 + this(createSSLContext( 1.247 + algorithm, keystore, keystorePassword, truststore, random, null), 1.248 + hostnameVerifier); 1.249 + } 1.250 + 1.251 + /** 1.252 + * @since 4.1 1.253 + */ 1.254 + public SSLSocketFactory( 1.255 + String algorithm, 1.256 + final KeyStore keystore, 1.257 + final String keystorePassword, 1.258 + final KeyStore truststore, 1.259 + final SecureRandom random, 1.260 + final TrustStrategy trustStrategy, 1.261 + final X509HostnameVerifier hostnameVerifier) 1.262 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.263 + this(createSSLContext( 1.264 + algorithm, keystore, keystorePassword, truststore, random, trustStrategy), 1.265 + hostnameVerifier); 1.266 + } 1.267 + 1.268 + public SSLSocketFactory( 1.269 + final KeyStore keystore, 1.270 + final String keystorePassword, 1.271 + final KeyStore truststore) 1.272 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.273 + this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 1.274 + } 1.275 + 1.276 + public SSLSocketFactory( 1.277 + final KeyStore keystore, 1.278 + final String keystorePassword) 1.279 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{ 1.280 + this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 1.281 + } 1.282 + 1.283 + public SSLSocketFactory( 1.284 + final KeyStore truststore) 1.285 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.286 + this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 1.287 + } 1.288 + 1.289 + /** 1.290 + * @since 4.1 1.291 + */ 1.292 + public SSLSocketFactory( 1.293 + final TrustStrategy trustStrategy, 1.294 + final X509HostnameVerifier hostnameVerifier) 1.295 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.296 + this(TLS, null, null, null, null, trustStrategy, hostnameVerifier); 1.297 + } 1.298 + 1.299 + /** 1.300 + * @since 4.1 1.301 + */ 1.302 + public SSLSocketFactory( 1.303 + final TrustStrategy trustStrategy) 1.304 + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { 1.305 + this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 1.306 + } 1.307 + 1.308 + public SSLSocketFactory(final SSLContext sslContext) { 1.309 + this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); 1.310 + } 1.311 + 1.312 + /** 1.313 + * @deprecated Use {@link #SSLSocketFactory(SSLContext)} 1.314 + */ 1.315 + @Deprecated 1.316 + public SSLSocketFactory( 1.317 + final SSLContext sslContext, final HostNameResolver nameResolver) { 1.318 + super(); 1.319 + this.socketfactory = sslContext.getSocketFactory(); 1.320 + this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; 1.321 + this.nameResolver = nameResolver; 1.322 + } 1.323 + 1.324 + /** 1.325 + * @since 4.1 1.326 + */ 1.327 + public SSLSocketFactory( 1.328 + final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) { 1.329 + super(); 1.330 + this.socketfactory = sslContext.getSocketFactory(); 1.331 + this.hostnameVerifier = hostnameVerifier; 1.332 + this.nameResolver = null; 1.333 + } 1.334 + 1.335 + private SSLSocketFactory() { 1.336 + this(createDefaultSSLContext()); 1.337 + } 1.338 + 1.339 + /** 1.340 + * @param params Optional parameters. Parameters passed to this method will have no effect. 1.341 + * This method will create a unconnected instance of {@link Socket} class. 1.342 + * @since 4.1 1.343 + */ 1.344 + public Socket createSocket(final HttpParams params) throws IOException { 1.345 + return this.socketfactory.createSocket(); 1.346 + } 1.347 + 1.348 + @Deprecated 1.349 + public Socket createSocket() throws IOException { 1.350 + return this.socketfactory.createSocket(); 1.351 + } 1.352 + 1.353 + /** 1.354 + * @since 4.1 1.355 + */ 1.356 + public Socket connectSocket( 1.357 + final Socket socket, 1.358 + final InetSocketAddress remoteAddress, 1.359 + final InetSocketAddress localAddress, 1.360 + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { 1.361 + if (remoteAddress == null) { 1.362 + throw new IllegalArgumentException("Remote address may not be null"); 1.363 + } 1.364 + if (params == null) { 1.365 + throw new IllegalArgumentException("HTTP parameters may not be null"); 1.366 + } 1.367 + Socket sock = socket != null ? socket : new Socket(); 1.368 + if (localAddress != null) { 1.369 + sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params)); 1.370 + sock.bind(localAddress); 1.371 + } 1.372 + 1.373 + int connTimeout = HttpConnectionParams.getConnectionTimeout(params); 1.374 + int soTimeout = HttpConnectionParams.getSoTimeout(params); 1.375 + 1.376 + try { 1.377 + sock.setSoTimeout(soTimeout); 1.378 + sock.connect(remoteAddress, connTimeout); 1.379 + } catch (SocketTimeoutException ex) { 1.380 + throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out"); 1.381 + } 1.382 + 1.383 + // HttpInetSocketAddress#toString() returns original hostname value of the remote address 1.384 + String hostname = remoteAddress.toString(); 1.385 + int port = remoteAddress.getPort(); 1.386 + String s = ":" + port; 1.387 + if (hostname.endsWith(s)) { 1.388 + hostname = hostname.substring(0, hostname.length() - s.length()); 1.389 + } 1.390 + 1.391 + SSLSocket sslsock; 1.392 + // Setup SSL layering if necessary 1.393 + if (sock instanceof SSLSocket) { 1.394 + sslsock = (SSLSocket) sock; 1.395 + } else { 1.396 + sslsock = (SSLSocket) this.socketfactory.createSocket(sock, hostname, port, true); 1.397 + } 1.398 + if (this.hostnameVerifier != null) { 1.399 + try { 1.400 + this.hostnameVerifier.verify(hostname, sslsock); 1.401 + // verifyHostName() didn't blowup - good! 1.402 + } catch (IOException iox) { 1.403 + // close the socket before re-throwing the exception 1.404 + try { sslsock.close(); } catch (Exception x) { /*ignore*/ } 1.405 + throw iox; 1.406 + } 1.407 + } 1.408 + return sslsock; 1.409 + } 1.410 + 1.411 + 1.412 + /** 1.413 + * Checks whether a socket connection is secure. 1.414 + * This factory creates TLS/SSL socket connections 1.415 + * which, by default, are considered secure. 1.416 + * <br/> 1.417 + * Derived classes may override this method to perform 1.418 + * runtime checks, for example based on the cypher suite. 1.419 + * 1.420 + * @param sock the connected socket 1.421 + * 1.422 + * @return <code>true</code> 1.423 + * 1.424 + * @throws IllegalArgumentException if the argument is invalid 1.425 + */ 1.426 + public boolean isSecure(final Socket sock) throws IllegalArgumentException { 1.427 + if (sock == null) { 1.428 + throw new IllegalArgumentException("Socket may not be null"); 1.429 + } 1.430 + // This instanceof check is in line with createSocket() above. 1.431 + if (!(sock instanceof SSLSocket)) { 1.432 + throw new IllegalArgumentException("Socket not created by this factory"); 1.433 + } 1.434 + // This check is performed last since it calls the argument object. 1.435 + if (sock.isClosed()) { 1.436 + throw new IllegalArgumentException("Socket is closed"); 1.437 + } 1.438 + return true; 1.439 + } 1.440 + 1.441 + /** 1.442 + * @since 4.1 1.443 + */ 1.444 + public Socket createLayeredSocket( 1.445 + final Socket socket, 1.446 + final String host, 1.447 + final int port, 1.448 + final boolean autoClose) throws IOException, UnknownHostException { 1.449 + SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket( 1.450 + socket, 1.451 + host, 1.452 + port, 1.453 + autoClose 1.454 + ); 1.455 + if (this.hostnameVerifier != null) { 1.456 + this.hostnameVerifier.verify(host, sslSocket); 1.457 + } 1.458 + // verifyHostName() didn't blowup - good! 1.459 + return sslSocket; 1.460 + } 1.461 + 1.462 + @Deprecated 1.463 + public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { 1.464 + if ( hostnameVerifier == null ) { 1.465 + throw new IllegalArgumentException("Hostname verifier may not be null"); 1.466 + } 1.467 + this.hostnameVerifier = hostnameVerifier; 1.468 + } 1.469 + 1.470 + public X509HostnameVerifier getHostnameVerifier() { 1.471 + return this.hostnameVerifier; 1.472 + } 1.473 + 1.474 + /** 1.475 + * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)} 1.476 + */ 1.477 + @Deprecated 1.478 + public Socket connectSocket( 1.479 + final Socket socket, 1.480 + final String host, int port, 1.481 + final InetAddress localAddress, int localPort, 1.482 + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { 1.483 + InetSocketAddress local = null; 1.484 + if (localAddress != null || localPort > 0) { 1.485 + // we need to bind explicitly 1.486 + if (localPort < 0) { 1.487 + localPort = 0; // indicates "any" 1.488 + } 1.489 + local = new InetSocketAddress(localAddress, localPort); 1.490 + } 1.491 + InetAddress remoteAddress; 1.492 + if (this.nameResolver != null) { 1.493 + remoteAddress = this.nameResolver.resolve(host); 1.494 + } else { 1.495 + remoteAddress = InetAddress.getByName(host); 1.496 + } 1.497 + InetSocketAddress remote = new InetSocketAddress(remoteAddress, port); 1.498 + return connectSocket(socket, remote, local, params); 1.499 + } 1.500 + 1.501 + /** 1.502 + * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)} 1.503 + */ 1.504 + @Deprecated 1.505 + public Socket createSocket( 1.506 + final Socket socket, 1.507 + final String host, int port, 1.508 + boolean autoClose) throws IOException, UnknownHostException { 1.509 + return createLayeredSocket(socket, host, port, autoClose); 1.510 + } 1.511 + 1.512 +}