diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactory.java b/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactory.java new file mode 100644 index 000000000..624745577 --- /dev/null +++ b/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactory.java @@ -0,0 +1,401 @@ +/* + * $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 + * . + * + * This class was copied from org.apache.http.conn.ssl, because it didn't have a suitable + * constructor. + */ + +package com.android.emailcommon.utility; + +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. + *. + *

+ * 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. + *

+ * + *

+ * 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. + *

+ * + *

+ * Use JDK keytool utility to import a trusted certificate and generate a truststore file: + *

+ *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
+ *    
+ *

+ * + *

+ * 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 + *

+ * + *

+ * Use the following sequence of actions to generate a keystore file + *

+ * + * @author Oleg Kalnichevski + * @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); + } + sslcontext = SSLContext.getInstance(algorithm); + sslcontext.init(keymanagers, trustmanagers, random); + socketfactory = 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(); + sslcontext = null; + this.socketfactory = socketfactory; + nameResolver = null; + } + + /** + * Creates the default SSL socket factory. + * This constructor is used exclusively to instantiate the factory for + * {@link #getSocketFactory getSocketFactory}. + */ + private SSLSocketFactory() { + super(); + sslcontext = null; + socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); + 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 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 (nameResolver != null) { + remoteAddress = new InetSocketAddress(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. + *
+ * Derived classes may override this method to perform + * runtime checks, for example based on the cypher suite. + * + * @param sock the connected socket + * + * @return true + * + * @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) 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; + } + +}