org.openhealthtools.openexchange.actorconfig.net.SecureSocketFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.openhealthtools.openexchange.actorconfig.net.SecureSocketFactory.java

Source

/**
 *  Copyright (c) 2009-2011 Misys Open Source Solutions (MOSS) and others
 *
 *  Licensed 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.
 *
 *  Contributors:
 *    Misys Open Source Solutions - initial API and implementation
 *    -
 */

package org.openhealthtools.openexchange.actorconfig.net;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;

import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/** Used by the connection factory to create customized secure sockets.
 * <p>
 * SecureSocketFactory 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>
 * SecureSocketFactory 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 misys.jks
 *    </pre>
 * Alternately, generate a pkcs12 keychain and use that as a truststore:
 *      <pre>
 *     openssl pkcs12 -in provided.cert -export -out misys.p12 -name provided
 *      </pre>
 * </p>
 * 
 * <p>
 * SecureSocketFactory 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>
 * <p>
 * Use JDK keytool utility to generate a new key, make sure the store has the same 
 * password as the key.  Then check to make sure that the key generation took.  You 
 * can use a keystore and a truststore, or one that does both.
 * </p>
 * <pre>
 *  keytool -genkey -v -alias "misys" -validity 365 -keystore misys.jks
 *  keytool -list -v -keystore my.keystore
 * </pre> 
 * <p>
 * The lame keytool tool can't import private keys, so if you need to use another
 * privte key (e.g. one given to you for IHE connectathon) then you need to use 
 * a different system.  Change it to pkcs12 format and make sure that you have the
 * filname end in .p12 then the factory wil deal with it correctly.  How to use OpenSSL
 * to generate the appropriate files:
 * </p>
 * <pre>
 *  openssl pkcs12 -in provided.cert -inkey provided.key -export -out misys.p12 -name provided
 * </pre>
 * <p>
 * That makes a single keystore for both the private key and the public certs.
 * </p>
 * <p>
 * Example of using custom protocol socket factory for a specific host:
 *     <pre>
 *     Protocol authhttps = new Protocol("https",  
 *          new SecureSocketFactory(
 *              new URL("file:my.keystore"), "mypassword",
 *              new URL("file:my.truststore"), "mypassword"), 443); 
 *
 *     HttpClient client = new HttpClient();
 *     client.getHostConfiguration().setHost("localhost", 443, authhttps);
 *     // use relative url only
 *     GetMethod httpget = new GetMethod("/");
 *     client.executeMethod(httpget);
 *     </pre>
 * </p>
 * <p>
 * Example of using custom protocol socket factory per default instead of the standard one:
 *     <pre>
 *     Protocol authhttps = new Protocol("https",  
 *          new SecureSocketFactory(
 *              new URL("file:my.keystore"), "mypassword",
 *              new URL("file:my.truststore"), "mypassword"), 443); 
 *     Protocol.registerProtocol("https", authhttps);
 *
 *     HttpClient client = new HttpClient();
 *     GetMethod httpget = new GetMethod("https://localhost/");
 *     client.executeMethod(httpget);
 *     </pre>
 * </p>
 * 
 * @author Josh Flachsbart
 * @version 2.0 - Oct 25, 2005
 */

public class SecureSocketFactory implements SecureProtocolSocketFactory {

    /** Log object for this class. */
    private static final Log LOG = LogFactory.getLog(SecureSocketFactory.class);

    private URL keystoreUrl = null;
    private String keystorePassword = null;
    private URL truststoreUrl = null;
    private String truststorePassword = null;
    private SSLContext sslcontext = null;
    private SecureConnectionDescription scd;

    /**
     * Constructor for HttpStreamHandler. Either a keystore or truststore file
     * must be given. Otherwise SSL context initialization error will result.
     *
      * @param scd The secure connection description
     */
    public SecureSocketFactory(SecureConnectionDescription scd) {
        super();

        this.keystoreUrl = scd.getKeyStore();
        this.keystorePassword = scd.getKeyStorePassword();
        this.truststoreUrl = scd.getTrustStore();
        this.truststorePassword = scd.getTrustStorePassword();
        this.scd = scd;
    }

    private SSLContext createSSLContext() throws IOException {
        try {
            LOG.info("Attempting to create ssl context.");
            KeyManager[] keymanagers = null;
            TrustManager[] trustmanagers = null;
            if (this.keystoreUrl != null) {
                KeyStore keystore = ConnectionCertificateHandler.createKeyStore(this.keystoreUrl,
                        this.keystorePassword);
                if (LOG.isDebugEnabled())
                    ConnectionCertificateHandler.printKeyCertificates(keystore);
                keymanagers = ConnectionCertificateHandler.createKeyManagers(keystore, this.keystorePassword);
            }
            if (this.truststoreUrl != null) {
                KeyStore keystore = ConnectionCertificateHandler.createKeyStore(this.truststoreUrl,
                        this.truststorePassword);
                if (LOG.isDebugEnabled())
                    ConnectionCertificateHandler.printTrustCerts(keystore);
                trustmanagers = ConnectionCertificateHandler.createTrustManagers(keystore, this.scd);
            }
            SSLContext sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(keymanagers, trustmanagers, null);
            return sslcontext;
        } catch (NoSuchAlgorithmException e) {
            LOG.error("NSA: " + e.getMessage(), e);
            throw new IOException("Unsupported algorithm exception: " + e.getMessage());
        } catch (KeyStoreException e) {
            LOG.error("Key Store: " + e.getMessage(), e);
            throw new IOException("Keystore exception: " + e.getMessage());
        } catch (GeneralSecurityException e) {
            LOG.error("General: " + e.getMessage(), e);
            throw new IOException("Key management exception: " + e.getMessage());
        } catch (IOException e) {
            LOG.error("I/O exception: " + e.getMessage(), e);
            throw new IOException("I/O error reading keystore/truststore file: " + e.getMessage());
        }
    }

    private SSLContext getSSLContext() throws IOException {
        if (this.sslcontext == null) {
            this.sslcontext = createSSLContext();
        }
        return this.sslcontext;
    }

    private void setAtnaProtocols(SSLSocket secureSocket) {
        secureSocket.setEnabledProtocols(new String[] { "TLSv1" });
        String[] strings = { //retired per CP 478 "SSL_RSA_WITH_NULL_SHA", 
                "TLS_RSA_WITH_AES_128_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA" };
        secureSocket.setEnabledCipherSuites(strings);
        // Useful debugging information:
        //secureSocket.setSoTimeout(1000);
        //String[] strings = secureSocket.getSupportedCipherSuites();
        //for (String s: strings) System.out.println(s);
        //strings = secureSocket.getEnabledCipherSuites();
        //for (String s: strings) System.out.println(s);
    }

    /**
     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
     */
    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) {
        Socket socket = null;
        try {
            socket = getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
            setAtnaProtocols((SSLSocket) socket);
        } catch (java.net.ConnectException e) {
            LOG.error("Connection was refused when connecting to socket.", e);
        } catch (IOException e) {
            LOG.error("I/O problem creating socket.", e);
        } catch (Exception e) {
            LOG.error("Problem creating socket.", e);
        }
        return socket;
    }

    /**
     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
     */
    public Socket createSocket(String host, int port) {
        Socket socket = null;
        try {
            socket = getSSLContext().getSocketFactory().createSocket(host, port);
            setAtnaProtocols((SSLSocket) socket);
        } catch (java.net.ConnectException e) {
            LOG.error("Connection was refused when connecting to socket.", e);
        } catch (IOException e) {
            LOG.error("I/O problem creating socket.", e);
        } catch (Exception e) {
            LOG.error("Problem creating socket.", e);
        }
        return socket;
    }

    /**
     * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
     */
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) {
        Socket lsocket = null;
        try {
            lsocket = getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
            setAtnaProtocols((SSLSocket) lsocket);
        } catch (java.net.ConnectException e) {
            LOG.error("Connection was refused when connecting to socket.", e);
        } catch (IOException e) {
            LOG.error("I/O problem creating socket.", e);
        } catch (Exception e) {
            LOG.error("Problem creating socket.", e);
        }
        return lsocket;
    }

    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort,
            HttpConnectionParams params) {
        Socket lsocket = null;
        try {
            lsocket = getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
            setAtnaProtocols((SSLSocket) lsocket);
        } catch (java.net.ConnectException e) {
            LOG.error("Connection was refused when connecting to socket.", e);
        } catch (IOException e) {
            LOG.error("I/O problem creating socket.", e);
        } catch (Exception e) {
            LOG.error("Problem creating socket.", e);
        }
        return lsocket;
    }

    /**
    * Extra socket creation for servers only.
    */
    public ServerSocket createServerSocket(int port) {
        javax.net.ssl.SSLServerSocket ss = null;
        try {
            ss = (javax.net.ssl.SSLServerSocket) getSSLContext().getServerSocketFactory().createServerSocket(port);
            ss.setNeedClientAuth(true);
            String[] strings = { //retired per CP 478 "SSL_RSA_WITH_NULL_SHA", 
                    "TLS_RSA_WITH_AES_128_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA" };
            ss.setEnabledCipherSuites(strings);
        } catch (IOException e) {
            LOG.error("I/O problem creating server socket.", e);
        }
        return ss;
    }
}