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