edu.vt.middleware.ldap.LdapTLSSocketFactory.java Source code

Java tutorial

Introduction

Here is the source code for edu.vt.middleware.ldap.LdapTLSSocketFactory.java

Source

/*
  $Id$
    
  Copyright (C) 2003-2009 Virginia Tech.
  All rights reserved.
    
  SEE LICENSE FOR MORE INFORMATION
    
  Author:  Middleware Services
  Email:   middleware@vt.edu
  Version: $Revision$
  Updated: $Date$
*/
package edu.vt.middleware.ldap;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <code>TLSSocketFactory</code> is an extension of SSLSocketFactory. It was
 * written to allow easy use of keystores and truststores. Note that {@link
 * #initialize()} must be called prior to using this socket factory. This means
 * that this class cannot be passed to implementations that expect the socket
 * factory to function immediately after construction.
 *
 * @author  Middleware Services
 * @version  $Revision$ $Date$
 */

public class LdapTLSSocketFactory extends SSLSocketFactory {

    /** Default SSL protocol, value is {@value}. */
    public static final String DEFAULT_PROTOCOL = "TLS";

    /** Default truststore name, value is {@value}. */
    public static final String DEFAULT_TRUSTSTORE_NAME = "/vt-ldap.truststore";

    /** Default truststore password, value is {@value}. */
    public static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit";

    /** Default truststore type, value is {@value}. */
    public static final String DEFAULT_TRUSTSTORE_TYPE = "JKS";

    /** Default keystore name, value is {@value}. */
    public static final String DEFAULT_KEYSTORE_NAME = "/vt-ldap.keystore";

    /** Default keystore password, value is {@value}. */
    public static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";

    /** Default keystore type, value is {@value}. */
    public static final String DEFAULT_KEYSTORE_TYPE = "JKS";

    /** Types of paths. */
    public enum PathType {

        /** File path location. */
        FILEPATH,

        /** Classpath location. */
        CLASSPATH
    }

    /** SSLSocketFactory used for creating SSL sockets. */
    protected SSLSocketFactory factory;

    /** Name of the truststore to use for the SSL connection. */
    private String trustStoreName = DEFAULT_TRUSTSTORE_NAME;

    /** Password needed to open the truststore. */
    private String trustStorePassword = DEFAULT_TRUSTSTORE_PASSWORD;

    /** Truststore path type. */
    private PathType trustStorePathType = PathType.CLASSPATH;

    /** Truststore type. */
    private String trustStoreType = DEFAULT_TRUSTSTORE_TYPE;

    /** Name of the keystore to use for the SSL connection. */
    private String keyStoreName = DEFAULT_KEYSTORE_NAME;

    /** Password needed to open the keystore. */
    private String keyStorePassword = DEFAULT_KEYSTORE_PASSWORD;

    /** Keystore path type. */
    private PathType keyStorePathType = PathType.CLASSPATH;

    /** Keystore type. */
    private String keyStoreType = DEFAULT_KEYSTORE_TYPE;

    /** Default constructor. */
    public LdapTLSSocketFactory() {
    }

    /**
     * Creates the underlying SSLContext using truststore and keystore attributes
     * and makes this factory ready for use. Must be called before factory can be
     * used.
     *
     * @throws  IOException  if the keystore cannot be loaded
     * @throws  GeneralSecurityException  if the SSLContext cannot be created
     */
    public void initialize() throws IOException, GeneralSecurityException {
        final SSLContext ctx = SSLContext.getInstance(DEFAULT_PROTOCOL);
        final TrustManager[] tm = this.initTrustManager(this.getTrustStoreStream(), this.getTrustStorePassword(),
                this.getTrustStoreType());
        final KeyManager[] km = this.initKeyManager(this.getKeyStoreStream(), this.getKeyStorePassword(),
                this.getKeyStoreType());
        ctx.init(km, tm, null);
        this.factory = ctx.getSocketFactory();
    }

    /**
     * This attempts to load the TrustManagers from the supplied <code>
     * InputStream</code> using the supplied password.
     *
     * @param  is  <code>InputStream</code> containing the truststore
     * @param  password  <code>String</code> to unlock the truststore
     * @param  storeType  <code>String</code> of truststore
     *
     * @return  <code>TrustManager[]</code>
     *
     * @throws  IOException  if the keystore cannot be loaded
     * @throws  GeneralSecurityException  if an errors occurs while loading the
     * TrustManagers
     */
    private TrustManager[] initTrustManager(final InputStream is, final String password, final String storeType)
            throws IOException, GeneralSecurityException {
        TrustManager[] tm = null;
        if (is != null) {
            final TrustManagerFactory tmf = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(this.loadKeyStore(is, password, storeType));
            tm = tmf.getTrustManagers();
        }
        return tm;
    }

    /**
     * This attempts to load the KeyManagers from the supplied <code>
     * InputStream</code> using the supplied password.
     *
     * @param  is  <code>InputStream</code> containing the keystore
     * @param  password  <code>String</code> to unlock the keystore
     * @param  storeType  <code>String</code> of keystore
     *
     * @return  <code>KeyManager[]</code>
     *
     * @throws  IOException  if the keystore cannot be loaded
     * @throws  GeneralSecurityException  if an errors occurs while loading the
     * KeyManagers
     */
    private KeyManager[] initKeyManager(final InputStream is, final String password, final String storeType)
            throws IOException, GeneralSecurityException {
        KeyManager[] km = null;
        if (is != null) {
            final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(this.loadKeyStore(is, password, storeType), password != null ? password.toCharArray() : null);
            km = kmf.getKeyManagers();
        }
        return km;
    }

    /**
     * This returns the name of the truststore to use.
     *
     * @return  <code>String</code> truststore name
     */
    public String getTrustStoreName() {
        return this.trustStoreName;
    }

    /**
     * This sets the name of the truststore to use.
     *
     * @param  s  <code>String</code> truststore name
     */
    public void setTrustStoreName(final String s) {
        this.trustStoreName = s;
    }

    /**
     * This gets the path type of the truststore.
     *
     * @return  <code>PathType</code> truststore path type
     */
    public PathType getTrustStorePathType() {
        return this.trustStorePathType;
    }

    /**
     * This sets the path type of the truststore.
     *
     * @param  pt  <code>PathType</code> truststore path type
     */
    public void setTrustStorePathType(final PathType pt) {
        this.trustStorePathType = pt;
    }

    /**
     * This returns the truststore as an <code>InputStream</code>. If the
     * truststore could not be loaded this method returns null.
     *
     * @return  <code>InputStream</code> truststore
     */
    protected InputStream getTrustStoreStream() {
        return this.getInputStream(this.trustStoreName, this.trustStorePathType);
    }

    /**
     * This returns the password for the truststore.
     *
     * @return  <code>String</code> truststore password
     */
    public String getTrustStorePassword() {
        return this.trustStorePassword;
    }

    /**
     * This sets the password for the truststore.
     *
     * @param  s  <code>String</code> truststore password
     */
    public void setTrustStorePassword(final String s) {
        this.trustStorePassword = s;
    }

    /**
     * This returns the type of the truststore.
     *
     * @return  <code>String</code> truststore type
     */
    public String getTrustStoreType() {
        return this.trustStoreType;
    }

    /**
     * This sets the type of the truststore.
     *
     * @param  s  <code>String</code> truststore type
     */
    public void setTrustStoreType(final String s) {
        this.trustStoreType = s;
    }

    /**
     * This returns the name of the keystore to use.
     *
     * @return  <code>String</code> keystore name
     */
    public String getKeyStoreName() {
        return this.keyStoreName;
    }

    /**
     * This sets the name of the keystore to use.
     *
     * @param  s  <code>String</code> keystore name
     */
    public void setKeyStoreName(final String s) {
        this.keyStoreName = s;
    }

    /**
     * This gets the path type of the keystore.
     *
     * @return  <code>PathType</code> keystore path type
     */
    public PathType getKeyStorePathType() {
        return this.keyStorePathType;
    }

    /**
     * This sets the path type of the keystore.
     *
     * @param  pt  <code>PathType</code> keystore path type
     */
    public void setKeyStorePathType(final PathType pt) {
        this.keyStorePathType = pt;
    }

    /**
     * This returns the keystore as an <code>InputStream</code>. If the keystore
     * could not be loaded this method returns null.
     *
     * @return  <code>InputStream</code> keystore
     */
    protected InputStream getKeyStoreStream() {
        return this.getInputStream(this.keyStoreName, this.keyStorePathType);
    }

    /**
     * This returns the password for the keystore.
     *
     * @return  <code>String</code> keystore password
     */
    public String getKeyStorePassword() {
        return this.keyStorePassword;
    }

    /**
     * This sets the password for the keystore.
     *
     * @param  s  <code>String</code> keystore password
     */
    public void setKeyStorePassword(final String s) {
        this.keyStorePassword = s;
    }

    /**
     * This returns the type of the keystore.
     *
     * @return  <code>String</code> keystore type
     */
    public String getKeyStoreType() {
        return this.keyStoreType;
    }

    /**
     * This sets the type of the keystore.
     *
     * @param  s  <code>String</code> keystore type
     */
    public void setKeyStoreType(final String s) {
        this.keyStoreType = s;
    }

    /**
     * This returns the underlying <code>SSLSocketFactory</code> that this class
     * uses for creating SSL Sockets.
     *
     * @return  <code>SSLSocketFactory</code>
     */
    public SSLSocketFactory getFactory() {
        return this.factory;
    }

    /**
     * This returns the default SSL socket factory.
     *
     * @return  <code>SocketFactory</code>
     */
    public static SocketFactory getDefault() {
        final LdapTLSSocketFactory sf = new LdapTLSSocketFactory();
        try {
            sf.initialize();
        } catch (IOException e) {
            final Log logger = LogFactory.getLog(LdapTLSSocketFactory.class);
            if (logger.isErrorEnabled()) {
                logger.error("Error loading keystore", e);
            }
        } catch (GeneralSecurityException e) {
            final Log logger = LogFactory.getLog(LdapTLSSocketFactory.class);
            if (logger.isErrorEnabled()) {
                logger.error("Error initializing socket factory", e);
            }
        }
        return sf;
    }

    /**
     * This returns a socket layered over an existing socket connected to the
     * named host, at the given port.
     *
     * @param  s  <code>Socket</code> existing socket
     * @param  host  <code>String</code> server hostname
     * @param  port  <code>int</code> server port
     * @param  autoClose  <code>boolean</code> close the underlying socket when
     * this socket is closed
     *
     * @return  <code>Socket</code> - connected to the specified host and port
     *
     * @throws  IOException  if an I/O error occurs when creating the socket
     */
    public Socket createSocket(final Socket s, final String host, final int port, final boolean autoClose)
            throws IOException {
        Socket socket = null;
        if (this.factory != null) {
            socket = this.factory.createSocket(s, host, port, autoClose);
        }
        return socket;
    }

    /**
     * This creates a socket and connects it to the specified port number at the
     * specified addres.
     *
     * @param  host  <code>InetAddress</code> server hostname
     * @param  port  <code>int</code> server port
     *
     * @return  <code>Socket</code> - connected to the specified host and port
     *
     * @throws  IOException  if an I/O error occurs when creating the socket
     */
    public Socket createSocket(final InetAddress host, final int port) throws IOException {
        Socket socket = null;
        if (this.factory != null) {
            socket = this.factory.createSocket(host, port);
        }
        return socket;
    }

    /**
     * This creates a socket and connect it to the specified port number at the
     * specified address. The socket will also be bound to the supplied local
     * address and port.
     *
     * @param  address  <code>InetAddress</code> server hostname
     * @param  port  <code>int</code> server port
     * @param  localAddress  <code>InetAddress</code> client hostname
     * @param  localPort  <code>int</code> client port
     *
     * @return  <code>Socket</code> - connected to the specified host and port
     *
     * @throws  IOException  if an I/O error occurs when creating the socket
     */
    public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress,
            final int localPort) throws IOException {
        Socket socket = null;
        if (this.factory != null) {
            socket = this.factory.createSocket(address, port, localAddress, localPort);
        }
        return socket;
    }

    /**
     * This creates a socket and connects it to the specified port number at the
     * specified addres.
     *
     * @param  host  <code>String</code> server hostname
     * @param  port  <code>int</code> server port
     *
     * @return  <code>Socket</code> - connected to the specified host and port
     *
     * @throws  IOException  if an I/O error occurs when creating the socket
     */
    public Socket createSocket(final String host, final int port) throws IOException {
        Socket socket = null;
        if (this.factory != null) {
            socket = this.factory.createSocket(host, port);
        }
        return socket;
    }

    /**
     * This creates a socket and connect it to the specified port number at the
     * specified address. The socket will also be bound to the supplied local
     * address and port.
     *
     * @param  host  <code>String</code> server hostname
     * @param  port  <code>int</code> server port
     * @param  localHost  <code>InetAddress</code> client hostname
     * @param  localPort  <code>int</code> client port
     *
     * @return  <code>Socket</code> - connected to the specified host and port
     *
     * @throws  IOException  if an I/O error occurs when creating the socket
     */
    public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort)
            throws IOException {
        Socket socket = null;
        if (this.factory != null) {
            socket = this.factory.createSocket(host, port, localHost, localPort);
        }
        return socket;
    }

    /**
     * This returns the list of cipher suites which are enabled by default.
     *
     * @return  <code>String[]</code> - array of the cipher suites
     */
    public String[] getDefaultCipherSuites() {
        String[] ciphers = null;
        if (this.factory != null) {
            ciphers = this.factory.getDefaultCipherSuites();
        }
        return ciphers;
    }

    /**
     * This returns the names of the cipher suites which could be enabled for use
     * on an SSL connection.
     *
     * @return  <code>String[]</code> - array of the cipher suites
     */
    public String[] getSupportedCipherSuites() {
        String[] ciphers = null;
        if (this.factory != null) {
            ciphers = this.factory.getSupportedCipherSuites();
        }
        return ciphers;
    }

    /**
     * This returns a keystore as an <code>InputStream</code>. If the keystore
     * could not be loaded this method returns null.
     *
     * @param  filename  <code>String</code> to read
     * @param  pt  <code>PathType</code> how to read file
     *
     * @return  <code>InputStream</code> keystore
     */
    private InputStream getInputStream(final String filename, final PathType pt) {
        final Log logger = LogFactory.getLog(LdapTLSSocketFactory.class);
        InputStream is = null;
        if (pt == PathType.CLASSPATH) {
            is = LdapTLSSocketFactory.class.getResourceAsStream(filename);
        } else if (pt == PathType.FILEPATH) {
            File file;
            try {
                file = new File(URI.create(filename));
            } catch (IllegalArgumentException e) {
                file = new File(filename);
            }
            try {
                is = new FileInputStream(file);
            } catch (IOException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Error loading keystore from " + filename, e);
                }
            }
        }
        if (is != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Successfully loaded " + filename + " from " + pt);
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to load " + filename + " from " + pt);
            }
        }
        return is;
    }

    /**
     * This attempts to load a keystore from the supplied <code>InputStream</code>
     * using the supplied password.
     *
     * @param  is  <code>InputStream</code> containing the keystore
     * @param  password  <code>String</code> to unlock the keystore
     * @param  storeType  <code>String</code> of keystore
     *
     * @return  <code>KeyStore</code>
     *
     * @throws  IOException  if the keystore cannot be loaded
     * @throws  GeneralSecurityException  if an errors occurs while loading the
     * KeyManagers
     */
    private KeyStore loadKeyStore(final InputStream is, final String password, final String storeType)
            throws IOException, GeneralSecurityException {
        KeyStore keystore = null;
        if (is != null) {
            String type = storeType;
            if (type == null) {
                type = KeyStore.getDefaultType();
            }
            keystore = KeyStore.getInstance(type);

            char[] pw = null;
            if (password != null) {
                pw = password.toCharArray();
            }
            keystore.load(is, pw);
        }
        return keystore;
    }
}