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.net.Socket;
027import java.security.GeneralSecurityException;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.Set;
035import java.util.StringTokenizer;
036import java.util.concurrent.atomic.AtomicReference;
037import javax.net.ssl.KeyManager;
038import javax.net.ssl.SSLContext;
039import javax.net.ssl.SSLSocket;
040import javax.net.ssl.SSLSocketFactory;
041import javax.net.ssl.SSLServerSocketFactory;
042import javax.net.ssl.TrustManager;
043
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.util.Debug;
047import com.unboundid.util.StaticUtils;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050
051import static com.unboundid.util.Validator.*;
052import static com.unboundid.util.ssl.SSLMessages.*;
053
054
055
056/**
057 * This class provides a simple interface for creating {@code SSLContext} and
058 * {@code SSLSocketFactory} instances, which may be used to create SSL-based
059 * connections, or secure existing connections with StartTLS.
060 * <BR><BR>
061 * <H2>Example 1</H2>
062 * The following example demonstrates the use of the SSL helper to create an
063 * SSL-based LDAP connection that will blindly trust any certificate that the
064 * server presents.  Using the {@code TrustAllTrustManager} is only recommended
065 * for testing purposes, since blindly trusting any certificate is not secure.
066 * <PRE>
067 * // Create an SSLUtil instance that is configured to trust any certificate,
068 * // and use it to create a socket factory.
069 * SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
070 * SSLSocketFactory sslSocketFactory = sslUtil.createSSLSocketFactory();
071 *
072 * // Establish a secure connection using the socket factory.
073 * LDAPConnection connection = new LDAPConnection(sslSocketFactory);
074 * connection.connect(serverAddress, serverSSLPort);
075 *
076 * // Process operations using the connection....
077 * RootDSE rootDSE = connection.getRootDSE();
078 *
079 * connection.close();
080 * </PRE>
081 * <BR>
082 * <H2>Example 2</H2>
083 * The following example demonstrates the use of the SSL helper to create a
084 * non-secure LDAP connection and then use the StartTLS extended operation to
085 * secure it.  It will use a trust store to determine whether to trust the
086 * server certificate.
087 * <PRE>
088 * // Establish a non-secure connection to the server.
089 * LDAPConnection connection = new LDAPConnection(serverAddress, serverPort);
090 *
091 * // Create an SSLUtil instance that is configured to trust certificates in
092 * // a specified trust store file, and use it to create an SSLContext that
093 * // will be used for StartTLS processing.
094 * SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath));
095 * SSLContext sslContext = sslUtil.createSSLContext();
096 *
097 * // Use the StartTLS extended operation to secure the connection.
098 * StartTLSExtendedRequest startTLSRequest =
099 *      new StartTLSExtendedRequest(sslContext);
100 * ExtendedResult startTLSResult;
101 * try
102 * {
103 *   startTLSResult = connection.processExtendedOperation(startTLSRequest);
104 * }
105 * catch (LDAPException le)
106 * {
107 *   startTLSResult = new ExtendedResult(le);
108 * }
109 * LDAPTestUtils.assertResultCodeEquals(startTLSResult, ResultCode.SUCCESS);
110 *
111 * // Process operations using the connection....
112 * RootDSE rootDSE = connection.getRootDSE();
113 *
114 * connection.close();
115 * </PRE>
116 */
117@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
118public final class SSLUtil
119{
120  /**
121   * The name of the system property that can be used to specify the initial
122   * value for the default SSL protocol that should be used.  If this is not
123   * set, then the default SSL protocol will be dynamically determined.  This
124   * can be overridden via the {@link #setDefaultSSLProtocol(String)} method.
125   */
126  public static final String PROPERTY_DEFAULT_SSL_PROTOCOL =
127       "com.unboundid.util.SSLUtil.defaultSSLProtocol";
128
129
130
131  /**
132   * The name of the system property that can be used to provide the initial
133   * set of enabled SSL protocols that should be used, as a comma-delimited
134   * list.  If this is not set, then the enabled SSL protocols will be
135   * dynamically determined.  This can be overridden via the
136   * {@link #setEnabledSSLProtocols(java.util.Collection)} method.
137   */
138  public static final String PROPERTY_ENABLED_SSL_PROTOCOLS =
139       "com.unboundid.util.SSLUtil.enabledSSLProtocols";
140
141
142
143  /**
144   * The default protocol string that will be used to create SSL contexts when
145   * no explicit protocol is specified.
146   */
147  private static final AtomicReference<String> DEFAULT_SSL_PROTOCOL =
148       new AtomicReference<String>("TLSv1");
149
150
151
152  /**
153   * The set of SSL protocols that will be enabled for use if available in SSL
154   * SSL sockets created within the LDAP SDK.
155   */
156  private static final AtomicReference<Set<String>> ENABLED_SSL_PROTOCOLS =
157       new AtomicReference<Set<String>>();
158
159
160
161  /**
162   * The set of SSL protocols, in all lowercase, that will be enabled for use if
163   * available in SSL sockets created within the LDAP SDK.
164   */
165  private static final AtomicReference<Set<String>>
166       LOWER_ENABLED_SSL_PROTOCOLS = new AtomicReference<Set<String>>();
167
168  static
169  {
170    configureSSLDefaults();
171  }
172
173
174
175  // The set of key managers to be used.
176  private final KeyManager[] keyManagers;
177
178  // The set of trust managers to be used.
179  private final TrustManager[] trustManagers;
180
181
182
183  /**
184   * Creates a new SSLUtil instance that will not have a custom key manager or
185   * trust manager.  It will not be able to provide a certificate to the server
186   * if one is requested, and it will only trust certificates signed by a
187   * predefined set of authorities.
188   */
189  public SSLUtil()
190  {
191    keyManagers   = null;
192    trustManagers = null;
193  }
194
195
196
197  /**
198   * Creates a new SSLUtil instance that will use the provided trust manager to
199   * determine whether to trust server certificates presented to the client.
200   * It will not be able to provide a certificate to the server if one is
201   * requested.
202   *
203   * @param  trustManager  The trust manager to use to determine whether to
204   *                       trust server certificates presented to the client.
205   *                       It may be {@code null} if the default set of trust
206   *                       managers should be used.
207   */
208  public SSLUtil(final TrustManager trustManager)
209  {
210    keyManagers = null;
211
212    if (trustManager == null)
213    {
214      trustManagers = null;
215    }
216    else
217    {
218      trustManagers = new TrustManager[] { trustManager };
219    }
220  }
221
222
223
224  /**
225   * Creates a new SSLUtil instance that will use the provided trust managers
226   * to determine whether to trust server certificates presented to the client.
227   * It will not be able to provide a certificate to the server if one is
228   * requested.
229   *
230   * @param  trustManagers  The set of trust managers to use to determine
231   *                        whether to trust server certificates presented to
232   *                        the client.  It may be {@code null} or empty if the
233   *                        default set of trust managers should be used.
234   */
235  public SSLUtil(final TrustManager[] trustManagers)
236  {
237    keyManagers = null;
238
239    if ((trustManagers == null) || (trustManagers.length == 0))
240    {
241      this.trustManagers = null;
242    }
243    else
244    {
245      this.trustManagers = trustManagers;
246    }
247  }
248
249
250
251  /**
252   * Creates a new SSLUtil instance that will use the provided key manager to
253   * obtain certificates to present to the server, and the provided trust
254   * manager to determine whether to trust server certificates presented to the
255   * client.
256   *
257   * @param  keyManager    The key manager to use to obtain certificates to
258   *                       present to the server if requested.  It may be
259   *                       {@code null} if no client certificates will be
260   *                       required or should be provided.
261   * @param  trustManager  The trust manager to use to determine whether to
262   *                       trust server certificates presented to the client.
263   *                       It may be {@code null} if the default set of trust
264   *                       managers should be used.
265   */
266  public SSLUtil(final KeyManager keyManager, final TrustManager trustManager)
267  {
268    if (keyManager == null)
269    {
270      keyManagers = null;
271    }
272    else
273    {
274      keyManagers = new KeyManager[] { keyManager };
275    }
276
277    if (trustManager == null)
278    {
279      trustManagers = null;
280    }
281    else
282    {
283      trustManagers = new TrustManager[] { trustManager };
284    }
285  }
286
287
288
289  /**
290   * Creates a new SSLUtil instance that will use the provided key managers to
291   * obtain certificates to present to the server, and the provided trust
292   * managers to determine whether to trust server certificates presented to the
293   * client.
294   *
295   * @param  keyManagers    The set of key managers to use to obtain
296   *                        certificates to present to the server if requested.
297   *                        It may be {@code null} or empty if no client
298   *                        certificates will be required or should be provided.
299   * @param  trustManagers  The set of trust managers to use to determine
300   *                        whether to trust server certificates presented to
301   *                        the client.  It may be {@code null} or empty if the
302   *                        default set of trust managers should be used.
303   */
304  public SSLUtil(final KeyManager[] keyManagers,
305                 final TrustManager[] trustManagers)
306  {
307    if ((keyManagers == null) || (keyManagers.length == 0))
308    {
309      this.keyManagers = null;
310    }
311    else
312    {
313      this.keyManagers = keyManagers;
314    }
315
316    if ((trustManagers == null) || (trustManagers.length == 0))
317    {
318      this.trustManagers = null;
319    }
320    else
321    {
322      this.trustManagers = trustManagers;
323    }
324  }
325
326
327
328  /**
329   * Retrieves the set of key managers configured for use by this class, if any.
330   *
331   * @return  The set of key managers configured for use by this class, or
332   *          {@code null} if none were provided.
333   */
334  public KeyManager[] getKeyManagers()
335  {
336    return keyManagers;
337  }
338
339
340
341  /**
342   * Retrieves the set of trust managers configured for use by this class, if
343   * any.
344   *
345   * @return  The set of trust managers configured for use by this class, or
346   *          {@code null} if none were provided.
347   */
348  public TrustManager[] getTrustManagers()
349  {
350    return trustManagers;
351  }
352
353
354
355  /**
356   * Creates an initialized SSL context created with the configured key and
357   * trust managers.  It will use the protocol returned by the
358   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
359   *
360   * @return  The created SSL context.
361   *
362   * @throws  GeneralSecurityException  If a problem occurs while creating or
363   *                                    initializing the SSL context.
364   */
365  public SSLContext createSSLContext()
366         throws GeneralSecurityException
367  {
368    return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
369  }
370
371
372
373  /**
374   * Creates an initialized SSL context created with the configured key and
375   * trust managers.  It will use the default provider.
376   *
377   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
378   *                   Architecture document, the set of supported protocols
379   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
380   *                   "SSLv2Hello".  It must not be {@code null}.
381   *
382   * @return  The created SSL context.
383   *
384   * @throws  GeneralSecurityException  If a problem occurs while creating or
385   *                                    initializing the SSL context.
386   */
387  public SSLContext createSSLContext(final String protocol)
388         throws GeneralSecurityException
389  {
390    ensureNotNull(protocol);
391
392    final SSLContext sslContext = SSLContext.getInstance(protocol);
393    sslContext.init(keyManagers, trustManagers, null);
394    return sslContext;
395  }
396
397
398
399  /**
400   * Creates an initialized SSL context created with the configured key and
401   * trust managers.
402   *
403   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
404   *                   Architecture document, the set of supported protocols
405   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
406   *                   "SSLv2Hello".  It must not be {@code null}.
407   * @param  provider  The name of the provider to use for cryptographic
408   *                   operations.  It must not be {@code null}.
409   *
410   * @return  The created SSL context.
411   *
412   * @throws  GeneralSecurityException  If a problem occurs while creating or
413   *                                    initializing the SSL context.
414   */
415  public SSLContext createSSLContext(final String protocol,
416                                     final String provider)
417         throws GeneralSecurityException
418  {
419    ensureNotNull(protocol, provider);
420
421    final SSLContext sslContext = SSLContext.getInstance(protocol, provider);
422    sslContext.init(keyManagers, trustManagers, null);
423    return sslContext;
424  }
425
426
427
428  /**
429   * Creates an SSL socket factory using the configured key and trust manager
430   * providers.  It will use the protocol returned by the
431   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
432   *
433   * @return  The created SSL socket factory.
434   *
435   * @throws  GeneralSecurityException  If a problem occurs while creating or
436   *                                    initializing the SSL socket factory.
437   */
438  public SSLSocketFactory createSSLSocketFactory()
439         throws GeneralSecurityException
440  {
441    return createSSLContext().getSocketFactory();
442  }
443
444
445
446  /**
447   * Creates an SSL socket factory with the configured key and trust managers.
448   * It will use the default provider.
449   *
450   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
451   *                   Architecture document, the set of supported protocols
452   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
453   *                   "SSLv2Hello".  It must not be {@code null}.
454   *
455   * @return  The created SSL socket factory.
456   *
457   * @throws  GeneralSecurityException  If a problem occurs while creating or
458   *                                    initializing the SSL socket factory.
459   */
460  public SSLSocketFactory createSSLSocketFactory(final String protocol)
461         throws GeneralSecurityException
462  {
463    return createSSLContext(protocol).getSocketFactory();
464  }
465
466
467
468  /**
469   * Creates an SSL socket factory with the configured key and trust managers.
470   *
471   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
472   *                   Architecture document, the set of supported protocols
473   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
474   *                   "SSLv2Hello".  It must not be {@code null}.
475   * @param  provider  The name of the provider to use for cryptographic
476   *                   operations.  It must not be {@code null}.
477   *
478   * @return  The created SSL socket factory.
479   *
480   * @throws  GeneralSecurityException  If a problem occurs while creating or
481   *                                    initializing the SSL socket factory.
482   */
483  public SSLSocketFactory createSSLSocketFactory(final String protocol,
484                                                 final String provider)
485         throws GeneralSecurityException
486  {
487    return createSSLContext(protocol, provider).getSocketFactory();
488  }
489
490
491
492  /**
493   * Creates an SSL server socket factory using the configured key and trust
494   * manager providers.  It will use the protocol returned by the
495   * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
496   *
497   * @return  The created SSL server socket factory.
498   *
499   * @throws  GeneralSecurityException  If a problem occurs while creating or
500   *                                    initializing the SSL server socket
501   *                                    factory.
502   */
503  public SSLServerSocketFactory createSSLServerSocketFactory()
504         throws GeneralSecurityException
505  {
506    return createSSLContext().getServerSocketFactory();
507  }
508
509
510
511  /**
512   * Creates an SSL server socket factory using the configured key and trust
513   * manager providers.  It will use the JVM-default provider.
514   *
515   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
516   *                   Architecture document, the set of supported protocols
517   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
518   *                   "SSLv2Hello".  It must not be {@code null}.
519   *
520   * @return  The created SSL server socket factory.
521   *
522   * @throws  GeneralSecurityException  If a problem occurs while creating or
523   *                                    initializing the SSL server socket
524   *                                    factory.
525   */
526  public SSLServerSocketFactory createSSLServerSocketFactory(
527                                     final String protocol)
528         throws GeneralSecurityException
529  {
530    return createSSLContext(protocol).getServerSocketFactory();
531  }
532
533
534
535  /**
536   * Creates an SSL server socket factory using the configured key and trust
537   * manager providers.
538   *
539   * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
540   *                   Architecture document, the set of supported protocols
541   *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
542   *                   "SSLv2Hello".  It must not be {@code null}.
543   * @param  provider  The name of the provider to use for cryptographic
544   *                   operations.  It must not be {@code null}.
545   *
546   * @return  The created SSL server socket factory.
547   *
548   * @throws  GeneralSecurityException  If a problem occurs while creating or
549   *                                    initializing the SSL server socket
550   *                                    factory.
551   */
552  public SSLServerSocketFactory createSSLServerSocketFactory(
553                                     final String protocol,
554                                     final String provider)
555         throws GeneralSecurityException
556  {
557    return createSSLContext(protocol, provider).getServerSocketFactory();
558  }
559
560
561
562  /**
563   * Retrieves the SSL protocol string that will be used by calls to
564   * {@link #createSSLContext()} that do not explicitly specify which protocol
565   * to use.
566   *
567   * @return  The SSL protocol string that will be used by calls to create an
568   *          SSL context that do not explicitly specify which protocol to use.
569   */
570  public static String getDefaultSSLProtocol()
571  {
572    return DEFAULT_SSL_PROTOCOL.get();
573  }
574
575
576
577  /**
578   * Specifies the SSL protocol string that will be used by calls to
579   * {@link #createSSLContext()} that do not explicitly specify which protocol
580   * to use.
581   *
582   * @param  defaultSSLProtocol  The SSL protocol string that will be used by
583   *                             calls to create an SSL context that do not
584   *                             explicitly specify which protocol to use.  It
585   *                             must not be {@code null}.
586   */
587  public static void setDefaultSSLProtocol(final String defaultSSLProtocol)
588  {
589    ensureNotNull(defaultSSLProtocol);
590
591    DEFAULT_SSL_PROTOCOL.set(defaultSSLProtocol);
592  }
593
594
595
596  /**
597   * Retrieves the set of SSL protocols that will be enabled for use, if
598   * available, for SSL sockets created within the LDAP SDK.
599   *
600   * @return  The set of SSL protocols that will be enabled for use, if
601   *          available, for SSL sockets created within the LDAP SDK.
602   */
603  public static Set<String> getEnabledSSLProtocols()
604  {
605    return ENABLED_SSL_PROTOCOLS.get();
606  }
607
608
609
610  /**
611   * Specifies the set of SSL protocols that will be enabled for use for SSL
612   * sockets created within the LDAP SDK.  When creating an SSL socket, the
613   * {@code SSLSocket.getSupportedProtocols} method will be used to determine
614   * which protocols are supported for that socket, and then the
615   * {@code SSLSocket.setEnabledProtocols} method will be used to enable those
616   * protocols which are listed as both supported by the socket and included in
617   * this set.  If the provided set is {@code null} or empty, then the default
618   * set of enabled protocols will be used.
619   *
620   * @param  enabledSSLProtocols  The set of SSL protocols that will be enabled
621   *                              for use for SSL sockets created within the
622   *                              LDAP SDK.  It may be {@code null} or empty to
623   *                              indicate that the JDK-default set of enabled
624   *                              protocols should be used for the socket.
625   */
626  public static void setEnabledSSLProtocols(
627                          final Collection<String> enabledSSLProtocols)
628  {
629    if (enabledSSLProtocols == null)
630    {
631      ENABLED_SSL_PROTOCOLS.set(Collections.<String>emptySet());
632      LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.<String>emptySet());
633    }
634    else
635    {
636      final HashSet<String> lowerProtocols =
637           new HashSet<String>(enabledSSLProtocols.size());
638      for (final String s : enabledSSLProtocols)
639      {
640        lowerProtocols.add(StaticUtils.toLowerCase(s));
641      }
642
643      ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
644           new HashSet<String>(enabledSSLProtocols)));
645      LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
646           new HashSet<String>(lowerProtocols)));
647    }
648  }
649
650
651
652  /**
653   * Updates the provided socket to apply the appropriate set of enabled SSL
654   * protocols.  This will only have any effect for sockets that are instances
655   * of {@code javax.net.ssl.SSLSocket}, but it is safe to call for any kind of
656   * {@code java.net.Socket}.  This should be called before attempting any
657   * communication over the socket, as
658   *
659   * @param  socket  The socket on which to apply the configured set of enabled
660   *                 SSL protocols.
661   *
662   * @throws  LDAPException  If {@link #getEnabledSSLProtocols} returns a
663   *                         non-empty set but none of the values in that set
664   *                         are supported by
665   */
666  public static void applyEnabledSSLProtocols(final Socket socket)
667         throws LDAPException
668  {
669    if ((socket == null) || (!(socket instanceof SSLSocket)))
670    {
671      return;
672    }
673
674    final Set<String> lowerEnabledProtocols = LOWER_ENABLED_SSL_PROTOCOLS.get();
675    if (lowerEnabledProtocols.isEmpty())
676    {
677      return;
678    }
679
680    final SSLSocket sslSocket = (SSLSocket) socket;
681    final String[] supportedProtocols = sslSocket.getSupportedProtocols();
682
683    final ArrayList<String> enabledList =
684         new ArrayList<String>(supportedProtocols.length);
685    for (final String supportedProtocol : supportedProtocols)
686    {
687      if (lowerEnabledProtocols.contains(
688           StaticUtils.toLowerCase(supportedProtocol)))
689      {
690        enabledList.add(supportedProtocol);
691      }
692    }
693
694    if (enabledList.isEmpty())
695    {
696      final StringBuilder enabledBuffer = new StringBuilder();
697      final Iterator<String> enabledIterator =
698           ENABLED_SSL_PROTOCOLS.get().iterator();
699      while (enabledIterator.hasNext())
700      {
701        enabledBuffer.append('\'');
702        enabledBuffer.append(enabledIterator.next());
703        enabledBuffer.append('\'');
704
705        if (enabledIterator.hasNext())
706        {
707          enabledBuffer.append(", ");
708        }
709      }
710
711      final StringBuilder supportedBuffer = new StringBuilder();
712      for (int i=0; i < supportedProtocols.length; i++)
713      {
714        if (i > 0)
715        {
716          supportedBuffer.append(", ");
717        }
718
719        supportedBuffer.append('\'');
720        supportedBuffer.append(supportedProtocols[i]);
721        supportedBuffer.append('\'');
722      }
723
724      throw new LDAPException(ResultCode.CONNECT_ERROR,
725           ERR_NO_ENABLED_SSL_PROTOCOLS_AVAILABLE_FOR_SOCKET.get(
726                enabledBuffer.toString(), supportedBuffer.toString(),
727                PROPERTY_ENABLED_SSL_PROTOCOLS,
728                SSLUtil.class.getName() + ".setEnabledSSLProtocols"));
729    }
730    else
731    {
732      final String[] enabledArray = new String[enabledList.size()];
733      sslSocket.setEnabledProtocols(enabledList.toArray(enabledArray));
734    }
735  }
736
737
738
739  /**
740   * Configures SSL default settings for the LDAP SDK.  This method is
741   * non-private for purposes of easier test coverage.
742   */
743  static void configureSSLDefaults()
744  {
745    // See if there is a system property that specifies what the default SSL
746    // protocol should be.  If not, then try to dynamically determine it.
747    final String defaultPropValue =
748         System.getProperty(PROPERTY_DEFAULT_SSL_PROTOCOL);
749    if ((defaultPropValue != null) && (defaultPropValue.length() > 0))
750    {
751      DEFAULT_SSL_PROTOCOL.set(defaultPropValue);
752    }
753    else
754    {
755      // Ideally, we should be able to discover the SSL protocol that offers the
756      // best mix of security and compatibility.  Unfortunately, Java SE 5
757      // doesn't expose the methods necessary to allow us to do that, but if the
758      // running JVM is Java SE 6 or later, then we can use reflection to invoke
759      // those methods and make the appropriate determination.  If we see that
760      // TLSv1.1 and/or TLSv1.2 are available, then we'll add those to the set
761      // of default enabled protocols.
762      try
763      {
764        final Method getDefaultMethod =
765             SSLContext.class.getMethod("getDefault");
766        final SSLContext defaultContext =
767             (SSLContext) getDefaultMethod.invoke(null);
768
769        final Method getSupportedParamsMethod =
770             SSLContext.class.getMethod("getSupportedSSLParameters");
771        final Object paramsObj =
772             getSupportedParamsMethod.invoke(defaultContext);
773
774        final Class<?> sslParamsClass =
775             Class.forName("javax.net.ssl.SSLParameters");
776        final Method getProtocolsMethod =
777             sslParamsClass.getMethod("getProtocols");
778        final String[] supportedProtocols =
779             (String[]) getProtocolsMethod.invoke(paramsObj);
780
781        final HashSet<String> protocolMap =
782             new HashSet<String>(Arrays.asList(supportedProtocols));
783        if (protocolMap.contains("TLSv1.2"))
784        {
785          DEFAULT_SSL_PROTOCOL.set("TLSv1.2");
786        }
787        else if (protocolMap.contains("TLSv1.1"))
788        {
789          DEFAULT_SSL_PROTOCOL.set("TLSv1.1");
790        }
791        else if (protocolMap.contains("TLSv1"))
792        {
793          DEFAULT_SSL_PROTOCOL.set("TLSv1");
794        }
795      }
796      catch (final Exception e)
797      {
798        Debug.debugException(e);
799      }
800    }
801
802    // A set to use for the default set of enabled protocols.  Unless otherwise
803    // specified via system property, we'll always enable TLSv1.  We may enable
804    // other protocols based on the default protocol.  The default set of
805    // enabled protocols will not include SSLv3 even if the JVM might otherwise
806    // include it as a default enabled protocol because of known security
807    // problems with SSLv3.
808    final HashSet<String> enabledProtocols = new HashSet<String>(10);
809    enabledProtocols.add("TLSv1");
810    if (DEFAULT_SSL_PROTOCOL.get().equals("TLSv1.2"))
811    {
812      enabledProtocols.add("TLSv1.1");
813      enabledProtocols.add("TLSv1.2");
814    }
815    else if (DEFAULT_SSL_PROTOCOL.get().equals("TLSv1.1"))
816    {
817      enabledProtocols.add("TLSv1.1");
818    }
819
820    // If there is a system property that specifies which enabled SSL protocols
821    // to use, then it will override the defaults.
822    final String enabledPropValue =
823         System.getProperty(PROPERTY_ENABLED_SSL_PROTOCOLS);
824    if ((enabledPropValue != null) && (enabledPropValue.length() > 0))
825    {
826      enabledProtocols.clear();
827
828      final StringTokenizer tokenizer = new StringTokenizer(enabledPropValue,
829           ", ", false);
830      while (tokenizer.hasMoreTokens())
831      {
832        final String token = tokenizer.nextToken();
833        if (token.length() > 0)
834        {
835          enabledProtocols.add(token);
836        }
837      }
838    }
839
840    // Get all-lowercase representations of the enabled protocols for more
841    // efficient comparisons.
842    final HashSet<String> lowerEnabledProtocols =
843         new HashSet<String>(enabledProtocols.size());
844    for (final String s : enabledProtocols)
845    {
846      lowerEnabledProtocols.add(StaticUtils.toLowerCase(s));
847    }
848
849    ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(enabledProtocols));
850    LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
851         lowerEnabledProtocols));
852  }
853}