| /* |
| * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $ |
| * $Revision: 659194 $ |
| * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $ |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| * This class was copied from org.apache.http.conn.ssl, because it didn't have a suitable |
| * constructor. |
| */ |
| |
| package com.android.exchange; |
| |
| import org.apache.http.conn.scheme.HostNameResolver; |
| import org.apache.http.conn.scheme.LayeredSocketFactory; |
| import org.apache.http.conn.ssl.AllowAllHostnameVerifier; |
| import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; |
| import org.apache.http.conn.ssl.StrictHostnameVerifier; |
| import org.apache.http.conn.ssl.X509HostnameVerifier; |
| import org.apache.http.params.HttpConnectionParams; |
| import org.apache.http.params.HttpParams; |
| |
| import javax.net.ssl.HttpsURLConnection; |
| import javax.net.ssl.KeyManager; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLSocket; |
| import javax.net.ssl.TrustManager; |
| import javax.net.ssl.TrustManagerFactory; |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.Socket; |
| import java.net.UnknownHostException; |
| import java.security.KeyManagementException; |
| import java.security.KeyStore; |
| import java.security.KeyStoreException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.security.UnrecoverableKeyException; |
| |
| /** |
| * Layered socket factory for TLS/SSL connections, based on JSSE. |
| *. |
| * <p> |
| * SSLSocketFactory can be used to validate the identity of the HTTPS |
| * server against a list of trusted certificates and to authenticate to |
| * the HTTPS server using a private key. |
| * </p> |
| * |
| * <p> |
| * SSLSocketFactory will enable server authentication when supplied with |
| * a {@link KeyStore truststore} file containg one or several trusted |
| * certificates. The client secure socket will reject the connection during |
| * the SSL session handshake if the target HTTPS server attempts to |
| * authenticate itself with a non-trusted certificate. |
| * </p> |
| * |
| * <p> |
| * Use JDK keytool utility to import a trusted certificate and generate a truststore file: |
| * <pre> |
| * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore |
| * </pre> |
| * </p> |
| * |
| * <p> |
| * SSLSocketFactory will enable client authentication when supplied with |
| * a {@link KeyStore keystore} file containg a private key/public certificate |
| * pair. The client secure socket will use the private key to authenticate |
| * itself to the target HTTPS server during the SSL session handshake if |
| * requested to do so by the server. |
| * The target HTTPS server will in its turn verify the certificate presented |
| * by the client in order to establish client's authenticity |
| * </p> |
| * |
| * <p> |
| * Use the following sequence of actions to generate a keystore file |
| * </p> |
| * <ul> |
| * <li> |
| * <p> |
| * Use JDK keytool utility to generate a new key |
| * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> |
| * For simplicity use the same password for the key as that of the keystore |
| * </p> |
| * </li> |
| * <li> |
| * <p> |
| * Issue a certificate signing request (CSR) |
| * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> |
| * </p> |
| * </li> |
| * <li> |
| * <p> |
| * Send the certificate request to the trusted Certificate Authority for signature. |
| * One may choose to act as her own CA and sign the certificate request using a PKI |
| * tool, such as OpenSSL. |
| * </p> |
| * </li> |
| * <li> |
| * <p> |
| * Import the trusted CA root certificate |
| * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> |
| * </p> |
| * </li> |
| * <li> |
| * <p> |
| * Import the PKCS#7 file containg the complete certificate chain |
| * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> |
| * </p> |
| * </li> |
| * <li> |
| * <p> |
| * Verify the content the resultant keystore file |
| * <pre>keytool -list -v -keystore my.keystore</pre> |
| * </p> |
| * </li> |
| * </ul> |
| * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> |
| * @author Julius Davies |
| */ |
| |
| public class SSLSocketFactory implements LayeredSocketFactory { |
| |
| public static final String TLS = "TLS"; |
| public static final String SSL = "SSL"; |
| public static final String SSLV2 = "SSLv2"; |
| |
| public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER |
| = new AllowAllHostnameVerifier(); |
| |
| public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER |
| = new BrowserCompatHostnameVerifier(); |
| |
| public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER |
| = new StrictHostnameVerifier(); |
| /** |
| * The factory using the default JVM settings for secure connections. |
| */ |
| private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory(); |
| |
| /** |
| * Gets an singleton instance of the SSLProtocolSocketFactory. |
| * @return a SSLProtocolSocketFactory |
| */ |
| public static SSLSocketFactory getSocketFactory() { |
| return DEFAULT_FACTORY; |
| } |
| |
| private final SSLContext sslcontext; |
| private final javax.net.ssl.SSLSocketFactory socketfactory; |
| private final HostNameResolver nameResolver; |
| private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; |
| |
| public SSLSocketFactory( |
| String algorithm, |
| final KeyStore keystore, |
| final String keystorePassword, |
| final KeyStore truststore, |
| final SecureRandom random, |
| final HostNameResolver nameResolver) |
| throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException |
| { |
| super(); |
| if (algorithm == null) { |
| algorithm = TLS; |
| } |
| KeyManager[] keymanagers = null; |
| if (keystore != null) { |
| keymanagers = createKeyManagers(keystore, keystorePassword); |
| } |
| TrustManager[] trustmanagers = null; |
| if (truststore != null) { |
| trustmanagers = createTrustManagers(truststore); |
| } |
| this.sslcontext = SSLContext.getInstance(algorithm); |
| this.sslcontext.init(keymanagers, trustmanagers, random); |
| this.socketfactory = this.sslcontext.getSocketFactory(); |
| this.nameResolver = nameResolver; |
| } |
| |
| public SSLSocketFactory( |
| final KeyStore keystore, |
| final String keystorePassword, |
| final KeyStore truststore) |
| throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException |
| { |
| this(TLS, keystore, keystorePassword, truststore, null, null); |
| } |
| |
| public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) |
| throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException |
| { |
| this(TLS, keystore, keystorePassword, null, null, null); |
| } |
| |
| public SSLSocketFactory(final KeyStore truststore) |
| throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException |
| { |
| this(TLS, null, null, truststore, null, null); |
| } |
| |
| /** |
| * Constructs an HttpClient SSLSocketFactory backed by the given JSSE |
| * SSLSocketFactory. |
| */ |
| public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) { |
| super(); |
| this.sslcontext = null; |
| this.socketfactory = socketfactory; |
| this.nameResolver = null; |
| } |
| |
| /** |
| * Creates the default SSL socket factory. |
| * This constructor is used exclusively to instantiate the factory for |
| * {@link #getSocketFactory getSocketFactory}. |
| */ |
| private SSLSocketFactory() { |
| super(); |
| this.sslcontext = null; |
| this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); |
| this.nameResolver = null; |
| } |
| |
| private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password) |
| throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { |
| if (keystore == null) { |
| throw new IllegalArgumentException("Keystore may not be null"); |
| } |
| KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( |
| KeyManagerFactory.getDefaultAlgorithm()); |
| kmfactory.init(keystore, password != null ? password.toCharArray(): null); |
| return kmfactory.getKeyManagers(); |
| } |
| |
| private static TrustManager[] createTrustManagers(final KeyStore keystore) |
| throws KeyStoreException, NoSuchAlgorithmException { |
| if (keystore == null) { |
| throw new IllegalArgumentException("Keystore may not be null"); |
| } |
| TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( |
| TrustManagerFactory.getDefaultAlgorithm()); |
| tmfactory.init(keystore); |
| return tmfactory.getTrustManagers(); |
| } |
| |
| |
| // non-javadoc, see interface org.apache.http.conn.SocketFactory |
| public Socket createSocket() |
| throws IOException { |
| |
| // the cast makes sure that the factory is working as expected |
| return (SSLSocket) this.socketfactory.createSocket(); |
| } |
| |
| |
| // non-javadoc, see interface org.apache.http.conn.SocketFactory |
| public Socket connectSocket( |
| final Socket sock, |
| final String host, |
| final int port, |
| final InetAddress localAddress, |
| int localPort, |
| final HttpParams params |
| ) throws IOException { |
| |
| if (host == null) { |
| throw new IllegalArgumentException("Target host may not be null."); |
| } |
| if (params == null) { |
| throw new IllegalArgumentException("Parameters may not be null."); |
| } |
| |
| SSLSocket sslsock = (SSLSocket) |
| ((sock != null) ? sock : createSocket()); |
| |
| if ((localAddress != null) || (localPort > 0)) { |
| |
| // we need to bind explicitly |
| if (localPort < 0) |
| localPort = 0; // indicates "any" |
| |
| InetSocketAddress isa = |
| new InetSocketAddress(localAddress, localPort); |
| sslsock.bind(isa); |
| } |
| |
| int connTimeout = HttpConnectionParams.getConnectionTimeout(params); |
| int soTimeout = HttpConnectionParams.getSoTimeout(params); |
| |
| InetSocketAddress remoteAddress; |
| if (this.nameResolver != null) { |
| remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); |
| } else { |
| remoteAddress = new InetSocketAddress(host, port); |
| } |
| |
| sslsock.connect(remoteAddress, connTimeout); |
| |
| sslsock.setSoTimeout(soTimeout); |
| try { |
| hostnameVerifier.verify(host, sslsock); |
| // verifyHostName() didn't blowup - good! |
| } catch (IOException iox) { |
| // close the socket before re-throwing the exception |
| try { sslsock.close(); } catch (Exception x) { /*ignore*/ } |
| throw iox; |
| } |
| |
| return sslsock; |
| } |
| |
| |
| /** |
| * Checks whether a socket connection is secure. |
| * This factory creates TLS/SSL socket connections |
| * which, by default, are considered secure. |
| * <br/> |
| * Derived classes may override this method to perform |
| * runtime checks, for example based on the cypher suite. |
| * |
| * @param sock the connected socket |
| * |
| * @return <code>true</code> |
| * |
| * @throws IllegalArgumentException if the argument is invalid |
| */ |
| public boolean isSecure(Socket sock) |
| throws IllegalArgumentException { |
| |
| if (sock == null) { |
| throw new IllegalArgumentException("Socket may not be null."); |
| } |
| // This instanceof check is in line with createSocket() above. |
| if (!(sock instanceof SSLSocket)) { |
| throw new IllegalArgumentException |
| ("Socket not created by this factory."); |
| } |
| // This check is performed last since it calls the argument object. |
| if (sock.isClosed()) { |
| throw new IllegalArgumentException("Socket is closed."); |
| } |
| |
| return true; |
| |
| } // isSecure |
| |
| |
| // non-javadoc, see interface LayeredSocketFactory |
| public Socket createSocket( |
| final Socket socket, |
| final String host, |
| final int port, |
| final boolean autoClose |
| ) throws IOException, UnknownHostException { |
| SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket( |
| socket, |
| host, |
| port, |
| autoClose |
| ); |
| hostnameVerifier.verify(host, sslSocket); |
| // verifyHostName() didn't blowup - good! |
| return sslSocket; |
| } |
| |
| public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { |
| if ( hostnameVerifier == null ) { |
| throw new IllegalArgumentException("Hostname verifier may not be null"); |
| } |
| this.hostnameVerifier = hostnameVerifier; |
| } |
| |
| public X509HostnameVerifier getHostnameVerifier() { |
| return hostnameVerifier; |
| } |
| |
| } |