# LdapSocketFactory.java

    /*
    ** Module   : LdapSocketFactory.java
    ** Abstract : Socket factory used to connect LDAP server using TLS.
    **
    ** Copyright (c) 2005-2017, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
    ** 001 SIY 20050424 NEW  @21018  Created initial version
    ** 002 SIY 20050516 CHG  @21197  Updated documentation.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.directory;

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.security.KeyStore;
    import java.util.Enumeration;

    import javax.net.SocketFactory;
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManagerFactory;

    import com.goldencode.p2j.cfg.ConfigurationException;

    /**
     * Implementation of custom socket factory for use with LDAP server accessed
     * via TLS connection.
     *
     * @author  SIY
     * @version 1.0
     */
    public class LdapSocketFactory
    extends SocketFactory
    {
       /** Actual implementation of the factory */
       private static SSLSocketFactory factory = null;

       /**
        * Construct an instance of socket factory which will used keys from the
        * provided password encrypted file.
        * <p>
        * The approach of handling stores and certificates is taken from
        * <code>TransportSecurity</code> class.
        *
        * @param   keyFile
        *          File name for the keystore.
        * @param   keyPass
        *          Password for keystore.
        * @param   trustFile
        *          File name for truststore.
        * @param   trustPass
        *          Password for truststore.
        * @param   subjectAlias
        *          Alias of the certificate to use.
        * @param   aliasPass
        *          Password for the certificate.
        * @throws  ConfigurationException
        *          In case of problems accessing required data.
        */
       static synchronized void initSocketFactory(String keyFile, String keyPass,
                                                  String trustFile,
                                                  String trustPass,
                                                  String subjectAlias,
                                                  String aliasPass)
       throws ConfigurationException
       {
          if (factory != null)
             return;

          KeyStore trustStore = null;
          KeyStore keyStore = null;

          try
          {
             // client side truststore initialization
             // this is a file specified in bootstrap config
             trustStore = KeyStore.getInstance("JKS");
             FileInputStream trustIn = new FileInputStream(trustFile);
             trustStore.load(trustIn, trustPass.toCharArray());
             trustIn.close();
          }
          catch (Exception e)
          {
             throw new ConfigurationException("misconfigured truststore "
                + trustFile, e);
          }

          try
          {
             // creating an in-memory keystore
             keyStore = KeyStore.getInstance("JKS");
             FileInputStream keyIn = new FileInputStream(keyFile);
             keyStore.load(keyIn, keyPass.toCharArray());
             keyIn.close();
          }
          catch (Exception e)
          {
             throw new ConfigurationException("misconfigured keystore " + keyFile,
                                              e);
          }

          try
          {
             // verifying the keystore: must have alias that matches subjectAlias
             // or, if not specified, exactly one key entry
             if (subjectAlias != null && !keyStore.isKeyEntry(subjectAlias))
                throw new ConfigurationException("misconfigured keystore: "
                   + "no matching alias for " + subjectAlias);

             if (subjectAlias == null)
             {
                Enumeration en = keyStore.aliases();
                int numkeys = 0;
                String alias = null;

                while (en.hasMoreElements())
                {
                   alias = (String)en.nextElement();
                   if (keyStore.isKeyEntry(alias))
                      numkeys++;
                   if (subjectAlias == null)
                      subjectAlias = alias;
                }
                if (numkeys == 0)
                   throw new ConfigurationException("misconfigured keystore: "
                      + "no private keys");
                if (numkeys > 1)
                   throw new ConfigurationException("misconfigured keystore: "
                      + "multiple private keys " + "without alias");
             }

             // normalizing the keystore by deleting all unnecessary entries
             Enumeration en = keyStore.aliases();

             while (en.hasMoreElements())
             {
                String alias = (String)en.nextElement();

                if (!subjectAlias.equals(alias))
                {
                   keyStore.deleteEntry(alias);
                }
             }
             // create a trust manager
             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
             tmf.init(trustStore);

             // create a key manager
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
             kmf.init(keyStore, aliasPass.toCharArray());

             SSLContext ctx = SSLContext.getInstance("TLS");

             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

             factory = ctx.getSocketFactory();
          }
          catch (Exception e)
          {
             throw new ConfigurationException("Unable to initialize SSLContext",
                                              e);
          }
       }

       /**
        * Return an instance of the LdapSocketFactory for use by client.
        *
        * @return  LdapSocketFactory instance.
        */
       public static SocketFactory getDefault()
       {
          return new LdapSocketFactory();
       }

       /**
        * {@inheritDoc}
        */
       public Socket createSocket(String arg0, int arg1)
       throws IOException,
          UnknownHostException
       {
          return factory.createSocket(arg0, arg1);
       }

       /**
        * {@inheritDoc}
        */
       public Socket createSocket(InetAddress arg0, int arg1)
       throws IOException
       {
          return factory.createSocket(arg0, arg1);
       }

       /**
        * {@inheritDoc}
        */
       public Socket createSocket(String arg0, int arg1, InetAddress arg2,
                                  int arg3)
       throws IOException,
          UnknownHostException
       {
          return factory.createSocket(arg0, arg1, arg2, arg3);
       }

       /**
        * {@inheritDoc}
        */
       public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2,
                                  int arg3)
       throws IOException
       {
          return factory.createSocket(arg0, arg1, arg2, arg3);
       }
    }