org.openhealthtools.openatna.net.SecureSocketFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.openhealthtools.openatna.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.openatna.net;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
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;

/**
 * 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/> <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/> <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/> <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/> <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);
 * <p/>
 *     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);
 * <p/>
 *     HttpClient client = new HttpClient();
 *     GetMethod httpget = new GetMethod("https://localhost/");
 *     client.executeMethod(httpget);
 *     </pre>
 * </p>
 *
 * @author Josh Flachsbart
 */

public class SecureSocketFactory {//implements SecureProtocolSocketFactory {

    /**
     * Log object for this class.
     */

    static Log log = LogFactory.getLog("org.openhealthtools.openatna.net.SecureSocketFactory");

    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.debug("Attempting to create ssl context.");
            KeyManager[] keymanagers = null;
            TrustManager[] trustmanagers = null;
            if (this.keystoreUrl == null) {
                throw new IOException("Cannot create SSL context without keystore");
            }
            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());
        }
    }

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

    public String[] getAtnaProtocols() {
        return new String[] { "TLSv1" };
    }

    public String[] getAtnaCipherSuites() {
        return new String[] { "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" };
    }

    private void setAtnaProtocols(SSLSocket secureSocket) {
        secureSocket.setEnabledProtocols(getAtnaProtocols());

        //String[] strings = {"SSL_RSA_WITH_NULL_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA"};
        secureSocket.setEnabledCipherSuites(getAtnaCipherSuites());
        // 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 = { "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;
    }
}