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