001/*
002 * Copyright 2008-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util.ssl;
022
023
024
025import java.lang.reflect.Method;
026import java.security.GeneralSecurityException;
027import java.util.Arrays;
028import java.util.HashSet;
029import java.util.concurrent.atomic.AtomicReference;
030import javax.net.ssl.KeyManager;
031import javax.net.ssl.SSLContext;
032import javax.net.ssl.SSLSocketFactory;
033import javax.net.ssl.SSLServerSocketFactory;
034import javax.net.ssl.TrustManager;
035
036import com.unboundid.util.Debug;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.util.Validator.*;
041
042
043
044/**
045 * This class provides a simple interface for creating {@code SSLContext} and
046 * {@code SSLSocketFactory} instances, which may be used to create SSL-based
047 * connections, or secure existing connections with StartTLS.
048 * <BR><BR>
049 * <H2>Example 1</H2>
050 * The following example demonstrates the use of the SSL helper to create an
051 * SSL-based LDAP connection that will blindly trust any certificate that the
052 * server presents.  Using the {@code TrustAllTrustManager} is only recommended
053 * for testing purposes, since blindly trusting any certificate is not secure.
054 * <PRE>
055 * // Create an SSLUtil instance that is configured to trust any certificate,
056 * // and use it to create a socket factory.
057 * SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
058 * SSLSocketFactory sslSocketFactory = sslUtil.createSSLSocketFactory();
059 *
060 * // Establish a secure connection using the socket factory.
061 * LDAPConnection connection = new LDAPConnection(sslSocketFactory);
062 * connection.connect(serverAddress, serverSSLPort);
063 *
064 * // Process operations using the connection....
065 * RootDSE rootDSE = connection.getRootDSE();
066 *
067 * connection.close();
068 * </PRE>
069 * <BR>
070 * <H2>Example 2</H2>
071 * The following example demonstrates the use of the SSL helper to create a
072 * non-secure LDAP connection and then use the StartTLS extended operation to
073 * secure it.  It will use a trust store to determine whether to trust the
074 * server certificate.
075 * <PRE>
076 * // Establish a non-secure connection to the server.
077 * LDAPConnection connection = new LDAPConnection(serverAddress, serverPort);
078 *
079 * // Create an SSLUtil instance that is configured to trust certificates in
080 * // a specified trust store file, and use it to create an SSLContext that
081 * // will be used for StartTLS processing.
082 * SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath));
083 * SSLContext sslContext = sslUtil.createSSLContext();
084 *
085 * // Use the StartTLS extended operation to secure the connection.
086 * StartTLSExtendedRequest startTLSRequest =
087 *      new StartTLSExtendedRequest(sslContext);
088 * ExtendedResult startTLSResult;
089 * try
090 * {
091 *   startTLSResult = connection.processExtendedOperation(startTLSRequest);
092 * }
093 * catch (LDAPException le)
094 * {
095 *   startTLSResult = new ExtendedResult(le);
096 * }
097 * LDAPTestUtils.assertResultCodeEquals(startTLSResult, ResultCode.SUCCESS);
098 *
099 * // Process operations using the connection....
100 * RootDSE rootDSE = connection.getRootDSE();
101 *
102 * connection.close();
103 * </PRE>
104 */
105@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
106public final class SSLUtil
107{
108  /**
109   * The name of the system property that can be used to specify the initial
110   * value for the default SSL protocol that should be used.  If this is not
111   * set, then the default SSL protocol will be dynamically determined.  This
112   * can be overridden via the {@link #setDefaultSSLProtocol(String)} method.
113   */
114  public static final String PROPERTY_DEFAULT_SSL_PROTOCOL =
115       "com.unboundid.util.SSLUtil.defaultSSLProtocol";
116
117
118
119  /**
120   * The default protocol string that will be used to create SSL contexts when
121   * no explicit protocol is specified.
122   */
123  private static final AtomicReference<String> DEFAULT_SSL_PROTOCOL =
124       new AtomicReference<String>("TLSv1");
125
126  static
127  {
128    // See if there is a system property that specifies what the default SSL
129    // protocol should be.  If not, then try to dynamically determine it.
130    final String propValue = System.getProperty(PROPERTY_DEFAULT_SSL_PROTOCOL);
131    if ((propValue != null) && (propValue.length() > 0))
132    {
133      DEFAULT_SSL_PROTOCOL.set(propValue);
134    }
135    else
136    {
137      // Ideally, we should be able to discover the SSL protocol that offers the
138      // best mix of security and compatibility.  Unfortunately, Java SE 5
139      // doesn't expose the methods necessary to allow us to do that, but if the
140      // running JVM is Java SE 6 or later, then we can use reflection to invoke
141      // those methods and make the appropriate determination.
142
143      try
144      {
145        final Method getDefaultMethod =
146             SSLContext.class.getMethod("getDefault");
147        final SSLContext defaultContext =
148             (SSLContext) getDefaultMethod.invoke(null);
149
150        final Method getSupportedParamsMethod =
151             SSLContext.class.getMethod("getSupportedSSLParameters");
152        final Object paramsObj =
153             getSupportedParamsMethod.invoke(defaultContext);
154
155        final Class<?> sslParamsClass =
156             Class.forName("javax.net.ssl.SSLParameters");
157        final Method getProtocolsMethod =
158             sslParamsClass.getMethod("getProtocols");
159        final String[] supportedProtocols =
160             (String[]) getProtocolsMethod.invoke(paramsObj);
161
162        final HashSet<String> protocolMap =
163             new HashSet<String>(Arrays.asList(supportedProtocols));
164        if (protocolMap.contains("TLSv1.2"))
165        {
166          DEFAULT_SSL_PROTOCOL.set("TLSv1.2");
167        }
168        else if (protocolMap.contains("TLSv1.1"))
169        {
170          DEFAULT_SSL_PROTOCOL.set("TLSv1.1");
171        }
172        else if (protocolMap.contains("TLSv1"))
173        {
174          DEFAULT_SSL_PROTOCOL.set("TLSv1");
175        }
176      }
177      catch (final Exception e)
178      {
179        Debug.debugException(e);
180      }
181    }
182  }
183
184
185
186  // The set of key managers to be used.
187  private final KeyManager[] keyManagers;
188
189  // The set of trust managers to be used.
190  private final TrustManager[] trustManagers;
191
192
193
194  /**
195   * Creates a new SSLUtil instance that will not have a custom key manager or
196   * trust manager.  It will not be able to provide a certificate to the server
197   * if one is requested, and it will only trust certificates signed by a
198   * predefined set of authorities.
199   */
200  public SSLUtil()
201  {
202    keyManagers   = null;
203    trustManagers = null;
204  }
205
206
207
208  /**
209   * Creates a new SSLUtil instance that will use the provided trust manager to
210   * determine whether to trust server certificates presented to the client.
211   * It will not be able to provide a certificate to the server if one is
212   * requested.
213   *
214   * @param  trustManager  The trust manager to use to determine whether to
215   *                       trust server certificates presented to the client.
216   *                       It may be {@code null} if the default set of trust
217   *                       managers should be used.
218   */
219  public SSLUtil(final TrustManager trustManager)
220  {
221    keyManagers = null;
222
223    if (trustManager == null)
224    {
225      trustManagers = null;
226    }
227    else
228    {
229      trustManagers = new TrustManager[] { trustManager };
230    }
231  }
232
233
234
235  /**
236   * Creates a new SSLUtil instance that will use the provided trust managers
237   * to determine whether to trust server certificates presented to the client.
238   * It will not be able to provide a certificate to the server if one is
239   * requested.
240   *
241   * @param  trustManagers  The set of trust managers to use to determine
242   *                        whether to trust server certificates presented to
243   *                        the client.  It may be {@code null} or empty if the
244   *                        default set of trust managers should be used.
245   */
246  public SSLUtil(final TrustManager[] trustManagers)
247  {
248    keyManagers = null;
249
250    if ((trustManagers == null) || (trustManagers.length == 0))
251    {
252      this.trustManagers = null;
253    }
254    else
255    {
256      this.trustManagers = trustManagers;
257    }
258  }
259
260
261
262  /**
263   * Creates a new SSLUtil instance that will use the provided key manager to
264   * obtain certificates to present to the server, and the provided trust
265   * manager to determine whether to trust server certificates presented to the
266   * client.
267   *
268   * @param  keyManager    The key manager to use to obtain certificates to
269   *                       present to the server if requested.  It may be
270   *                       {@code null} if no client certificates will be
271   *                       required or should be provided.
272   * @param  trustManager  The trust manager to use to determine whether to
273   *                       trust server certificates presented to the client.
274   *                       It may be {@code null} if the default set of trust
275   *                       managers should be used.
276   */
277  public SSLUtil(final KeyManager keyManager, final TrustManager trustManager)
278  {
279    if (keyManager == null)
280    {
281      keyManagers = null;
282    }
283    else
284    {
285      keyManagers = new KeyManager[] { keyManager };
286    }
287
288    if (trustManager == null)
289    {
290      trustManagers = null;
291    }
292    else
293    {
294      trustManagers = new TrustManager[] { trustManager };
295    }
296  }
297
298
299
300  /**
301   * Creates a new SSLUtil instance that will use the provided key managers to
302   * obtain certificates to present to the server, and the provided trust
303   * managers to determine whether to trust server certificates presented to the
304   * client.
305   *
306   * @param  keyManagers    The set of key managers to use to obtain
307   *                        certificates to present to the server if requested.
308   *                        It may be {@code null} or empty if no client
309   *                        certificates will be required or should be provided.
310   * @param  trustManagers  The set of trust managers to use to determine
311   *                        whether to trust server certificates presented to
312   *                        the client.  It may be {@code null} or empty if the
313   *                        default set of trust managers should be used.
314   */
315  public SSLUtil(final KeyManager[] keyManagers,
316                 final TrustManager[] trustManagers)
317  {
318    if ((keyManagers == null) || (keyManagers.length == 0))
319    {
320      this.keyManagers = null;
321    }
322    else
323    {
324      this.keyManagers = keyManagers;
325    }
326
327    if ((trustManagers == null) || (trustManagers.length == 0))
328    {
329      this.trustManagers = null;
330    }
331    else
332    {
333      this.trustManagers = trustManagers;
334    }
335  }
336
337
338
339  /**
340   * Retrieves the set of key managers configured for use by this class, if any.
341   *
342   * @return  The set of key managers configured for use by this class, or
343   *          {@code null} if none were provided.
344   */
345  public KeyManager[] getKeyManagers()
346  {
347    return keyManagers;
348  }
349
350
351
352  /**
353   * Retrieves the set of trust managers configured for use by this class, if
354   * any.
355   *
356   * @return  The set of trust managers configured for use by this class, or
357   *          {@code null} if none were provided.
358   */
359  public TrustManager[] getTrustManagers()
360  {
361    return trustManagers;
362  }
363
364
365
366  /**
367   * Creates an initialized SSL context created with the configured key and
368   * trust managers.  It will use the protocol returned by the
369   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
370   *
371   * @return  The created SSL context.
372   *
373   * @throws  GeneralSecurityException  If a problem occurs while creating or
374   *                                    initializing the SSL context.
375   */
376  public SSLContext createSSLContext()
377         throws GeneralSecurityException
378  {
379    return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
380  }
381
382
383
384  /**
385   * Creates an initialized SSL context created with the configured key and
386   * trust managers.  It will use the default provider.
387   *
388   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
389   *                   Architecture document, the set of supported protocols
390   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
391   *                   "SSLv2Hello".  It must not be {@code null}.
392   *
393   * @return  The created SSL context.
394   *
395   * @throws  GeneralSecurityException  If a problem occurs while creating or
396   *                                    initializing the SSL context.
397   */
398  public SSLContext createSSLContext(final String protocol)
399         throws GeneralSecurityException
400  {
401    ensureNotNull(protocol);
402
403    final SSLContext sslContext = SSLContext.getInstance(protocol);
404    sslContext.init(keyManagers, trustManagers, null);
405    return sslContext;
406  }
407
408
409
410  /**
411   * Creates an initialized SSL context created with the configured key and
412   * trust managers.
413   *
414   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
415   *                   Architecture document, the set of supported protocols
416   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
417   *                   "SSLv2Hello".  It must not be {@code null}.
418   * @param  provider  The name of the provider to use for cryptographic
419   *                   operations.  It must not be {@code null}.
420   *
421   * @return  The created SSL context.
422   *
423   * @throws  GeneralSecurityException  If a problem occurs while creating or
424   *                                    initializing the SSL context.
425   */
426  public SSLContext createSSLContext(final String protocol,
427                                     final String provider)
428         throws GeneralSecurityException
429  {
430    ensureNotNull(protocol, provider);
431
432    final SSLContext sslContext = SSLContext.getInstance(protocol, provider);
433    sslContext.init(keyManagers, trustManagers, null);
434    return sslContext;
435  }
436
437
438
439  /**
440   * Creates an SSL socket factory using the configured key and trust manager
441   * providers.  It will use the protocol returned by the
442   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
443   *
444   * @return  The created SSL socket factory.
445   *
446   * @throws  GeneralSecurityException  If a problem occurs while creating or
447   *                                    initializing the SSL socket factory.
448   */
449  public SSLSocketFactory createSSLSocketFactory()
450         throws GeneralSecurityException
451  {
452    return createSSLContext().getSocketFactory();
453  }
454
455
456
457  /**
458   * Creates an SSL socket factory with the configured key and trust managers.
459   * It will use the default provider.
460   *
461   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
462   *                   Architecture document, the set of supported protocols
463   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
464   *                   "SSLv2Hello".  It must not be {@code null}.
465   *
466   * @return  The created SSL socket factory.
467   *
468   * @throws  GeneralSecurityException  If a problem occurs while creating or
469   *                                    initializing the SSL socket factory.
470   */
471  public SSLSocketFactory createSSLSocketFactory(final String protocol)
472         throws GeneralSecurityException
473  {
474    return createSSLContext(protocol).getSocketFactory();
475  }
476
477
478
479  /**
480   * Creates an SSL socket factory with the configured key and trust managers.
481   *
482   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
483   *                   Architecture document, the set of supported protocols
484   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
485   *                   "SSLv2Hello".  It must not be {@code null}.
486   * @param  provider  The name of the provider to use for cryptographic
487   *                   operations.  It must not be {@code null}.
488   *
489   * @return  The created SSL socket factory.
490   *
491   * @throws  GeneralSecurityException  If a problem occurs while creating or
492   *                                    initializing the SSL socket factory.
493   */
494  public SSLSocketFactory createSSLSocketFactory(final String protocol,
495                                                 final String provider)
496         throws GeneralSecurityException
497  {
498    return createSSLContext(protocol, provider).getSocketFactory();
499  }
500
501
502
503  /**
504   * Creates an SSL server socket factory using the configured key and trust
505   * manager providers.  It will use the protocol returned by the
506   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
507   *
508   * @return  The created SSL server socket factory.
509   *
510   * @throws  GeneralSecurityException  If a problem occurs while creating or
511   *                                    initializing the SSL server socket
512   *                                    factory.
513   */
514  public SSLServerSocketFactory createSSLServerSocketFactory()
515         throws GeneralSecurityException
516  {
517    return createSSLContext().getServerSocketFactory();
518  }
519
520
521
522  /**
523   * Creates an SSL server socket factory using the configured key and trust
524   * manager providers.  It will use the JVM-default provider.
525   *
526   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
527   *                   Architecture document, the set of supported protocols
528   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
529   *                   "SSLv2Hello".  It must not be {@code null}.
530   *
531   * @return  The created SSL server socket factory.
532   *
533   * @throws  GeneralSecurityException  If a problem occurs while creating or
534   *                                    initializing the SSL server socket
535   *                                    factory.
536   */
537  public SSLServerSocketFactory createSSLServerSocketFactory(
538                                     final String protocol)
539         throws GeneralSecurityException
540  {
541    return createSSLContext(protocol).getServerSocketFactory();
542  }
543
544
545
546  /**
547   * Creates an SSL server socket factory using the configured key and trust
548   * manager providers.
549   *
550   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
551   *                   Architecture document, the set of supported protocols
552   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
553   *                   "SSLv2Hello".  It must not be {@code null}.
554   * @param  provider  The name of the provider to use for cryptographic
555   *                   operations.  It must not be {@code null}.
556   *
557   * @return  The created SSL server socket factory.
558   *
559   * @throws  GeneralSecurityException  If a problem occurs while creating or
560   *                                    initializing the SSL server socket
561   *                                    factory.
562   */
563  public SSLServerSocketFactory createSSLServerSocketFactory(
564                                     final String protocol,
565                                     final String provider)
566         throws GeneralSecurityException
567  {
568    return createSSLContext(protocol, provider).getServerSocketFactory();
569  }
570
571
572
573  /**
574   * Retrieves the SSL protocol string that will be used by calls to
575   * {@link #createSSLContext()} that do not explicitly specify which protocol
576   * to use.
577   *
578   * @return  The SSL protocol string that will be used by calls to create an
579   *          SSL context that do not explicitly specify which protocol to use.
580   */
581  public static String getDefaultSSLProtocol()
582  {
583    return DEFAULT_SSL_PROTOCOL.get();
584  }
585
586
587
588  /**
589   * Specifies the SSL protocol string that will be used by calls to
590   * {@link #createSSLContext()} that do not explicitly specify which protocol
591   * to use.
592   *
593   * @param  defaultSSLProtocol  The SSL protocol string that will be used by
594   *                             calls to create an SSL context that do not
595   *                             explicitly specify which protocol to use.  It
596   *                             must not be {@code null}.
597   */
598  public static void setDefaultSSLProtocol(final String defaultSSLProtocol)
599  {
600    ensureNotNull(defaultSSLProtocol);
601
602    DEFAULT_SSL_PROTOCOL.set(defaultSSLProtocol);
603  }
604}