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