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