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