/* * $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 @Override 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 @Override 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 */ @Override 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 @Override 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; } }