Java tutorial
/* * $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/>. * */ package info.guardianproject.net; 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; 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 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.HttpParams; /** * 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 ModSSLSocketFactory 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 ModSSLSocketFactory DEFAULT_FACTORY = null; /** * Gets an singleton instance of the SSLProtocolSocketFactory. * @return a SSLProtocolSocketFactory */ public static ModSSLSocketFactory getSocketFactory(String proxyhost, int proxyport) { if (DEFAULT_FACTORY == null) { DEFAULT_FACTORY = new ModSSLSocketFactory(proxyhost, proxyport); } 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; private SocksSocketFactory mSocksSocketFactory = null; public ModSSLSocketFactory(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 ModSSLSocketFactory(final KeyStore keystore, final String keystorePassword, final KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { this(TLS, keystore, keystorePassword, truststore, null, null); } public ModSSLSocketFactory(final KeyStore keystore, final String keystorePassword) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { this(TLS, keystore, keystorePassword, null, null, null); } public ModSSLSocketFactory(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. * * @hide */ public ModSSLSocketFactory(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 ModSSLSocketFactory(String host, int port) { super(); this.sslcontext = null; this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); //this.nameResolver = null; this.mSocksSocketFactory = new SocksSocketFactory(host, port); } 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 { // SSL Sockets don't work at the moment. //throw new SSLException("SSL socket functionality broken"); // the cast makes sure that the factory is working as expected //return (SSLSocket) this.socketfactory.createSocket(); return new Socket(); //return null; } // 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."); } //Socket underlying = (Socket) // ((sock != null) ? sock : createSocket()); Socket underlying = sock; if (underlying == null) underlying = new Socket(); mSocksSocketFactory.connectSocket(underlying, host, port, localAddress, localPort, params); SSLSocket sslsock = (SSLSocket) this.socketfactory.createSocket(underlying, host, port, true); 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(0); 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; } public class SSLException extends IOException { private static final long serialVersionUID = 1L; public SSLException(String msg) { super(msg); } }; }