001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tools;
028    
029    
030    import java.io.FileInputStream;
031    import java.io.IOException;
032    import java.net.Socket;
033    import java.security.KeyStore;
034    import java.security.KeyStoreException;
035    import java.security.Provider;
036    import javax.net.ssl.KeyManager;
037    import javax.net.ssl.KeyManagerFactory;
038    import javax.net.ssl.SSLContext;
039    import javax.net.ssl.SSLSocketFactory;
040    import javax.net.ssl.TrustManager;
041    import javax.net.ssl.TrustManagerFactory;
042    import javax.net.ssl.X509TrustManager;
043    
044    import org.opends.server.extensions.BlindTrustManagerProvider;
045    import org.opends.server.util.ExpirationCheckTrustManager;
046    import org.opends.server.util.SelectableCertificateKeyManager;
047    
048    import static org.opends.messages.ToolMessages.*;
049    import static org.opends.server.loggers.debug.DebugLogger.*;
050    import org.opends.server.loggers.debug.DebugTracer;
051    import org.opends.server.types.DebugLogLevel;
052    
053    
054    /**
055     * This class provides SSL connection related utility functions.
056     */
057    public class SSLConnectionFactory
058    {
059      /**
060       * The tracer object for the debug logger.
061       */
062      private static final DebugTracer TRACER = getTracer();
063    
064    
065      private SSLSocketFactory sslSocketFactory = null;
066    
067      /**
068       * Constructor for the SSL connection factory.
069       */
070      public SSLConnectionFactory()
071      {
072      }
073    
074      /**
075       * Initialize the connection factory by creating the key and
076       * trust managers for the SSL connection.
077       *
078       * @param  trustAll            Indicates whether to blindly trust all
079       *                             certificates.
080       * @param  keyStorePath        The path to the key store file.
081       * @param  keyStorePassword    The PIN to use to access the key store
082       *                             contents.
083       * @param  clientAlias         The alias to use for the client certificate.
084       * @param  trustStorePath      The path to the trust store file.
085       * @param  trustStorePassword  The PIN to use to access the trust store
086       *                             contents.
087       *
088       * @throws  SSLConnectionException  If a problem occurs while initializing the
089       *                                  connection factory.
090       */
091      public void init(boolean trustAll, String keyStorePath,
092                       String keyStorePassword, String clientAlias,
093                       String trustStorePath, String trustStorePassword)
094             throws SSLConnectionException
095      {
096        try
097        {
098          SSLContext ctx = SSLContext.getInstance("TLS");
099          KeyManager[] keyManagers = null;
100          TrustManager[] trustManagers = null;
101    
102          if(trustAll)
103          {
104            BlindTrustManagerProvider blindTrustProvider =
105                new BlindTrustManagerProvider();
106            trustManagers = blindTrustProvider.getTrustManagers();
107          } else if (trustStorePath == null) {
108            trustManagers = PromptTrustManager.getTrustManagers();
109          } else
110          {
111            TrustManager[] tmpTrustManagers =
112                 getTrustManagers(KeyStore.getDefaultType(), null, trustStorePath,
113                                  trustStorePassword);
114            trustManagers = new TrustManager[tmpTrustManagers.length];
115            for (int i=0; i < trustManagers.length; i++)
116            {
117              trustManagers[i] =
118                   new ExpirationCheckTrustManager((X509TrustManager)
119                                                   tmpTrustManagers[i]);
120            }
121          }
122          if(keyStorePath != null)
123          {
124            keyManagers = getKeyManagers(KeyStore.getDefaultType(), null,
125                              keyStorePath, keyStorePassword);
126    
127            if (clientAlias != null)
128            {
129              keyManagers = SelectableCertificateKeyManager.wrap(keyManagers,
130                                                                 clientAlias);
131            }
132          }
133    
134          ctx.init(keyManagers, trustManagers, new java.security.SecureRandom());
135          sslSocketFactory = ctx.getSocketFactory();
136        } catch(Exception e)
137        {
138          throw new SSLConnectionException(
139                  ERR_TOOLS_CANNOT_CREATE_SSL_CONNECTION.get(e.getMessage()), e);
140        }
141      }
142    
143      /**
144       * Create the SSL socket connection to the specified host.
145       *
146       * @param  hostName    The address of the system to which the connection
147       *                     should be established.
148       * @param  portNumber  The port number to which the connection should be
149       *                     established.
150       *
151       * @return  The SSL socket established to the specified host.
152       *
153       * @throws  SSLConnectionException  If a problem occurs while performing SSL
154       *                                  negotiation.
155       *
156       * @throws  IOException  If a problem occurs while attempting to communicate
157       *                       with the server.
158       */
159      public Socket createSocket(String hostName, int portNumber)
160          throws SSLConnectionException, IOException
161      {
162        if(sslSocketFactory == null)
163        {
164    
165          throw new SSLConnectionException(
166                  ERR_TOOLS_SSL_CONNECTION_NOT_INITIALIZED.get());
167        }
168        return sslSocketFactory.createSocket(hostName, portNumber);
169      }
170    
171      /**
172       * Create the SSL socket connection to the specified host layered over
173       * an existing socket.
174       *
175       * @param  s           The socket to use for the existing connection.
176       * @param  hostName    The address of the system to which the connection
177       *                     should be established.
178       * @param  portNumber  The port number to which the connection should be
179       *                     established.
180       * @param  autoClose   Indicates whether the underlying connection should be
181       *                     automatically closed when the SSL session is ended.
182       *
183       * @return  The SSL socket established to the specified host.
184       *
185       * @throws  SSLConnectionException  If a problem occurs while performing SSL
186       *                                  negotiation.
187       *
188       * @throws  IOException  If a problem occurs while attempting to communicate
189       *                       with the server.
190       */
191      public Socket createSocket(Socket s, String hostName, int portNumber,
192                                 boolean autoClose)
193             throws SSLConnectionException, IOException
194      {
195        if(sslSocketFactory == null)
196        {
197    
198          throw new SSLConnectionException(
199                  ERR_TOOLS_SSL_CONNECTION_NOT_INITIALIZED.get());
200        }
201        return sslSocketFactory.createSocket(s, hostName, portNumber, autoClose);
202      }
203    
204      /**
205       * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
206       * interactions requiring access to a key manager.
207       *
208       * @param  keyStoreType  The key store type to use with the specified file.
209       * @param  provider      The provider to use when accessing the key store.
210       * @param  keyStoreFile  The path to the file containing the key store data.
211       * @param  keyStorePass  The PIN needed to access the key store contents.
212       *
213       * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
214       *          interactions requiring access to a key manager.
215       *
216       * @throws  KeyStoreException  If a problem occurs while interacting with the
217       *                             key store.
218       *
219       * @throws  SSLConnectionException  If a problem occurs while trying to load
220       *                                 key store file.
221       */
222    
223      private KeyManager[] getKeyManagers(String keyStoreType,
224                                          Provider provider,
225                                          String keyStoreFile,
226                                          String keyStorePass)
227              throws KeyStoreException, SSLConnectionException
228      {
229        if(keyStoreFile == null)
230        {
231          // Lookup the file name through the JDK property.
232          keyStoreFile = getKeyStore();
233        }
234    
235        if(keyStorePass == null)
236        {
237          // Lookup the keystore PIN through the JDK property.
238          keyStorePass = getKeyStorePIN();
239        }
240    
241        KeyStore ks = null;
242        if(provider != null)
243        {
244          ks = KeyStore.getInstance(keyStoreType, provider);
245        } else
246        {
247          ks = KeyStore.getInstance(keyStoreType);
248        }
249    
250        char[] keyStorePIN = null;
251        if(keyStorePass != null)
252        {
253          keyStorePIN = keyStorePass.toCharArray();
254        }
255    
256        try
257        {
258          FileInputStream inputStream = new FileInputStream(keyStoreFile);
259          ks.load(inputStream, keyStorePIN);
260          inputStream.close();
261    
262        } catch(Exception e)
263        {
264          if (debugEnabled())
265          {
266            TRACER.debugCaught(DebugLogLevel.ERROR, e);
267          }
268    
269          throw new SSLConnectionException(
270                  ERR_TOOLS_CANNOT_LOAD_KEYSTORE_FILE.get(keyStoreFile), e);
271        }
272    
273        try
274        {
275          String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
276          KeyManagerFactory keyManagerFactory =
277            KeyManagerFactory.getInstance(keyManagerAlgorithm);
278    
279          keyManagerFactory.init(ks, keyStorePIN);
280          return keyManagerFactory.getKeyManagers();
281        } catch(Exception ke)
282        {
283          if (debugEnabled())
284          {
285            TRACER.debugCaught(DebugLogLevel.ERROR, ke);
286          }
287    
288          throw new SSLConnectionException(
289                  ERR_TOOLS_CANNOT_INIT_KEYMANAGER.get(keyStoreFile), ke);
290        }
291    
292      }
293    
294    
295      /**
296       * Retrieves a set of <CODE>TrustManager</CODE> objects that may be used for
297       * interactions requiring access to a trust manager.
298       *
299       * @param  trustStoreType  The trust store type to use with the specified
300       *                         file.
301       * @param  provider        The provider to use when accessing the trust store.
302       * @param  trustStoreFile  The path to the file containing the trust store
303       *                         data.
304       * @param  trustStorePass  The PIN needed to access the trust store contents.
305       *
306       * @return  A set of <CODE>TrustManager</CODE> objects that may be used for
307       *          interactions requiring access to a trust manager.
308       *
309       * @throws  KeyStoreException  If a problem occurs while interacting with the
310       *                             trust store.
311       *
312       * @throws  SSLConnectionException  If a problem occurs while trying to load
313       *                                 trust store file.
314       */
315      private TrustManager[] getTrustManagers(String trustStoreType,
316                                                Provider provider,
317                                                String trustStoreFile,
318                                                String trustStorePass)
319          throws KeyStoreException, SSLConnectionException
320      {
321        if(trustStoreFile == null)
322        {
323          trustStoreFile = getTrustStore();
324          // No trust store file available.
325          if(trustStoreFile == null)
326          {
327            return null;
328          }
329        }
330    
331        if(trustStorePass == null)
332        {
333          trustStorePass = getTrustStorePIN();
334        }
335    
336        KeyStore trustStore = null;
337        if(provider != null)
338        {
339          trustStore = KeyStore.getInstance(trustStoreType, provider);
340        } else
341        {
342          trustStore = KeyStore.getInstance(trustStoreType);
343        }
344    
345        char[] trustStorePIN = null;
346        if(trustStorePass != null)
347        {
348          trustStorePIN = trustStorePass.toCharArray();
349        }
350    
351        try
352        {
353          FileInputStream inputStream = new FileInputStream(trustStoreFile);
354          trustStore.load(inputStream, trustStorePIN);
355          inputStream.close();
356        } catch(Exception e)
357        {
358          if (debugEnabled())
359          {
360            TRACER.debugCaught(DebugLogLevel.ERROR, e);
361          }
362    
363          throw new SSLConnectionException(
364                  ERR_TOOLS_CANNOT_LOAD_TRUSTSTORE_FILE.get(trustStoreFile), e);
365        }
366    
367        try
368        {
369          String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
370          TrustManagerFactory trustManagerFactory =
371            TrustManagerFactory.getInstance(trustManagerAlgorithm);
372    
373          trustManagerFactory.init(trustStore);
374          return trustManagerFactory.getTrustManagers();
375        } catch(Exception ke)
376        {
377          if (debugEnabled())
378          {
379            TRACER.debugCaught(DebugLogLevel.ERROR, ke);
380          }
381    
382          throw new SSLConnectionException(
383                  ERR_TOOLS_CANNOT_INIT_TRUSTMANAGER.get(trustStoreFile), ke);
384        }
385    
386      }
387    
388      /**
389       * Read the KeyStore PIN from the JSSE system property.
390       *
391       * @return  The PIN that should be used to access the key store.
392       */
393    
394       private String getKeyStorePIN()
395       {
396        return System.getProperty("javax.net.ssl.keyStorePassword");
397       }
398    
399      /**
400       * Read the TrustStore PIN from the JSSE system property.
401       *
402       * @return  The PIN that should be used to access the trust store.
403       */
404    
405       private String getTrustStorePIN()
406       {
407        return System.getProperty("javax.net.ssl.trustStorePassword");
408       }
409    
410      /**
411       * Read the KeyStore from the JSSE system property.
412       *
413       * @return  The path to the key store file.
414       */
415    
416       private String getKeyStore()
417       {
418        return System.getProperty("javax.net.ssl.keyStore");
419       }
420    
421      /**
422       * Read the TrustStore from the JSSE system property.
423       *
424       * @return  The path to the trust store file.
425       */
426    
427       private String getTrustStore()
428       {
429        return System.getProperty("javax.net.ssl.trustStore");
430       }
431    
432    }
433