001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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     */
021    package com.unboundid.util.ssl;
022    
023    
024    
025    import java.lang.reflect.Method;
026    import java.net.Socket;
027    import java.security.GeneralSecurityException;
028    import java.util.ArrayList;
029    import java.util.Arrays;
030    import java.util.Collection;
031    import java.util.Collections;
032    import java.util.HashSet;
033    import java.util.Iterator;
034    import java.util.Set;
035    import java.util.StringTokenizer;
036    import java.util.concurrent.atomic.AtomicReference;
037    import javax.net.ssl.KeyManager;
038    import javax.net.ssl.SSLContext;
039    import javax.net.ssl.SSLSocket;
040    import javax.net.ssl.SSLSocketFactory;
041    import javax.net.ssl.SSLServerSocketFactory;
042    import javax.net.ssl.TrustManager;
043    
044    import com.unboundid.ldap.sdk.LDAPException;
045    import com.unboundid.ldap.sdk.ResultCode;
046    import com.unboundid.util.Debug;
047    import com.unboundid.util.StaticUtils;
048    import com.unboundid.util.ThreadSafety;
049    import com.unboundid.util.ThreadSafetyLevel;
050    
051    import static com.unboundid.util.Validator.*;
052    import 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)
118    public 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    }