001    /*
002     * Copyright 2008-2014 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2014 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
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    
049    import static com.unboundid.util.Validator.*;
050    import static com.unboundid.util.ssl.SSLMessages.*;
051    
052    
053    
054    /**
055     * This class provides a simple interface for creating {@code SSLContext} and
056     * {@code SSLSocketFactory} instances, which may be used to create SSL-based
057     * connections, or secure existing connections with StartTLS.
058     * <BR><BR>
059     * <H2>Example 1</H2>
060     * The following example demonstrates the use of the SSL helper to create an
061     * SSL-based LDAP connection that will blindly trust any certificate that the
062     * server presents.  Using the {@code TrustAllTrustManager} is only recommended
063     * for testing purposes, since blindly trusting any certificate is not secure.
064     * <PRE>
065     * // Create an SSLUtil instance that is configured to trust any certificate,
066     * // and use it to create a socket factory.
067     * SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
068     * SSLSocketFactory sslSocketFactory = sslUtil.createSSLSocketFactory();
069     *
070     * // Establish a secure connection using the socket factory.
071     * LDAPConnection connection = new LDAPConnection(sslSocketFactory);
072     * connection.connect(serverAddress, serverSSLPort);
073     *
074     * // Process operations using the connection....
075     * RootDSE rootDSE = connection.getRootDSE();
076     *
077     * connection.close();
078     * </PRE>
079     * <BR>
080     * <H2>Example 2</H2>
081     * The following example demonstrates the use of the SSL helper to create a
082     * non-secure LDAP connection and then use the StartTLS extended operation to
083     * secure it.  It will use a trust store to determine whether to trust the
084     * server certificate.
085     * <PRE>
086     * // Establish a non-secure connection to the server.
087     * LDAPConnection connection = new LDAPConnection(serverAddress, serverPort);
088     *
089     * // Create an SSLUtil instance that is configured to trust certificates in
090     * // a specified trust store file, and use it to create an SSLContext that
091     * // will be used for StartTLS processing.
092     * SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath));
093     * SSLContext sslContext = sslUtil.createSSLContext();
094     *
095     * // Use the StartTLS extended operation to secure the connection.
096     * StartTLSExtendedRequest startTLSRequest =
097     *      new StartTLSExtendedRequest(sslContext);
098     * ExtendedResult startTLSResult;
099     * try
100     * {
101     *   startTLSResult = connection.processExtendedOperation(startTLSRequest);
102     * }
103     * catch (LDAPException le)
104     * {
105     *   startTLSResult = new ExtendedResult(le);
106     * }
107     * LDAPTestUtils.assertResultCodeEquals(startTLSResult, ResultCode.SUCCESS);
108     *
109     * // Process operations using the connection....
110     * RootDSE rootDSE = connection.getRootDSE();
111     *
112     * connection.close();
113     * </PRE>
114     */
115    public final class SSLUtil
116    {
117      /**
118       * The name of the system property that can be used to specify the initial
119       * value for the default SSL protocol that should be used.  If this is not
120       * set, then the default SSL protocol will be dynamically determined.  This
121       * can be overridden via the {@code setDefaultSSLProtocol(String)} method.
122       */
123      public static final String PROPERTY_DEFAULT_SSL_PROTOCOL =
124           "com.unboundid.util.SSLUtil.defaultSSLProtocol";
125    
126    
127    
128      /**
129       * The name of the system property that can be used to provide the initial
130       * set of enabled SSL protocols that should be used, as a comma-delimited
131       * list.  If this is not set, then the enabled SSL protocols will be
132       * dynamically determined.  This can be overridden via the
133       * {@code setEnabledSSLProtocols(java.util.Collection)} method.
134       */
135      public static final String PROPERTY_ENABLED_SSL_PROTOCOLS =
136           "com.unboundid.util.SSLUtil.enabledSSLProtocols";
137    
138    
139    
140      /**
141       * The default protocol string that will be used to create SSL contexts when
142       * no explicit protocol is specified.
143       */
144      private static final AtomicReference<String> DEFAULT_SSL_PROTOCOL =
145           new AtomicReference<String>("TLSv1");
146    
147    
148    
149      /**
150       * The set of SSL protocols that will be enabled for use if available in SSL
151       * SSL sockets created within the LDAP SDK.
152       */
153      private static final AtomicReference<Set<String>> ENABLED_SSL_PROTOCOLS =
154           new AtomicReference<Set<String>>();
155    
156    
157    
158      /**
159       * The set of SSL protocols, in all lowercase, that will be enabled for use if
160       * available in SSL sockets created within the LDAP SDK.
161       */
162      private static final AtomicReference<Set<String>>
163           LOWER_ENABLED_SSL_PROTOCOLS = new AtomicReference<Set<String>>();
164    
165      static
166      {
167        configureSSLDefaults();
168      }
169    
170    
171    
172      // The set of key managers to be used.
173      private final KeyManager[] keyManagers;
174    
175      // The set of trust managers to be used.
176      private final TrustManager[] trustManagers;
177    
178    
179    
180      /**
181       * Creates a new SSLUtil instance that will not have a custom key manager or
182       * trust manager.  It will not be able to provide a certificate to the server
183       * if one is requested, and it will only trust certificates signed by a
184       * predefined set of authorities.
185       */
186      public SSLUtil()
187      {
188        keyManagers   = null;
189        trustManagers = null;
190      }
191    
192    
193    
194      /**
195       * Creates a new SSLUtil instance that will use the provided trust manager to
196       * determine whether to trust server certificates presented to the client.
197       * It will not be able to provide a certificate to the server if one is
198       * requested.
199       *
200       * @param  trustManager  The trust manager to use to determine whether to
201       *                       trust server certificates presented to the client.
202       *                       It may be {@code null} if the default set of trust
203       *                       managers should be used.
204       */
205      public SSLUtil(final TrustManager trustManager)
206      {
207        keyManagers = null;
208    
209        if (trustManager == null)
210        {
211          trustManagers = null;
212        }
213        else
214        {
215          trustManagers = new TrustManager[] { trustManager };
216        }
217      }
218    
219    
220    
221      /**
222       * Creates a new SSLUtil instance that will use the provided trust managers
223       * to determine whether to trust server certificates presented to the client.
224       * It will not be able to provide a certificate to the server if one is
225       * requested.
226       *
227       * @param  trustManagers  The set of trust managers to use to determine
228       *                        whether to trust server certificates presented to
229       *                        the client.  It may be {@code null} or empty if the
230       *                        default set of trust managers should be used.
231       */
232      public SSLUtil(final TrustManager[] trustManagers)
233      {
234        keyManagers = null;
235    
236        if ((trustManagers == null) || (trustManagers.length == 0))
237        {
238          this.trustManagers = null;
239        }
240        else
241        {
242          this.trustManagers = trustManagers;
243        }
244      }
245    
246    
247    
248      /**
249       * Creates a new SSLUtil instance that will use the provided key manager to
250       * obtain certificates to present to the server, and the provided trust
251       * manager to determine whether to trust server certificates presented to the
252       * client.
253       *
254       * @param  keyManager    The key manager to use to obtain certificates to
255       *                       present to the server if requested.  It may be
256       *                       {@code null} if no client certificates will be
257       *                       required or should be provided.
258       * @param  trustManager  The trust manager to use to determine whether to
259       *                       trust server certificates presented to the client.
260       *                       It may be {@code null} if the default set of trust
261       *                       managers should be used.
262       */
263      public SSLUtil(final KeyManager keyManager, final TrustManager trustManager)
264      {
265        if (keyManager == null)
266        {
267          keyManagers = null;
268        }
269        else
270        {
271          keyManagers = new KeyManager[] { keyManager };
272        }
273    
274        if (trustManager == null)
275        {
276          trustManagers = null;
277        }
278        else
279        {
280          trustManagers = new TrustManager[] { trustManager };
281        }
282      }
283    
284    
285    
286      /**
287       * Creates a new SSLUtil instance that will use the provided key managers to
288       * obtain certificates to present to the server, and the provided trust
289       * managers to determine whether to trust server certificates presented to the
290       * client.
291       *
292       * @param  keyManagers    The set of key managers to use to obtain
293       *                        certificates to present to the server if requested.
294       *                        It may be {@code null} or empty if no client
295       *                        certificates will be required or should be provided.
296       * @param  trustManagers  The set of trust managers to use to determine
297       *                        whether to trust server certificates presented to
298       *                        the client.  It may be {@code null} or empty if the
299       *                        default set of trust managers should be used.
300       */
301      public SSLUtil(final KeyManager[] keyManagers,
302                     final TrustManager[] trustManagers)
303      {
304        if ((keyManagers == null) || (keyManagers.length == 0))
305        {
306          this.keyManagers = null;
307        }
308        else
309        {
310          this.keyManagers = keyManagers;
311        }
312    
313        if ((trustManagers == null) || (trustManagers.length == 0))
314        {
315          this.trustManagers = null;
316        }
317        else
318        {
319          this.trustManagers = trustManagers;
320        }
321      }
322    
323    
324    
325      /**
326       * Retrieves the set of key managers configured for use by this class, if any.
327       *
328       * @return  The set of key managers configured for use by this class, or
329       *          {@code null} if none were provided.
330       */
331      public KeyManager[] getKeyManagers()
332      {
333        return keyManagers;
334      }
335    
336    
337    
338      /**
339       * Retrieves the set of trust managers configured for use by this class, if
340       * any.
341       *
342       * @return  The set of trust managers configured for use by this class, or
343       *          {@code null} if none were provided.
344       */
345      public TrustManager[] getTrustManagers()
346      {
347        return trustManagers;
348      }
349    
350    
351    
352      /**
353       * Creates an initialized SSL context created with the configured key and
354       * trust managers.  It will use the protocol returned by the
355       * {@code getDefaultSSLProtocol} method and the JVM-default provider.
356       *
357       * @return  The created SSL context.
358       *
359       * @throws  GeneralSecurityException  If a problem occurs while creating or
360       *                                    initializing the SSL context.
361       */
362      public SSLContext createSSLContext()
363             throws GeneralSecurityException
364      {
365        return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
366      }
367    
368    
369    
370      /**
371       * Creates an initialized SSL context created with the configured key and
372       * trust managers.  It will use the default provider.
373       *
374       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
375       *                   Architecture document, the set of supported protocols
376       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
377       *                   "SSLv2Hello".  It must not be {@code null}.
378       *
379       * @return  The created SSL context.
380       *
381       * @throws  GeneralSecurityException  If a problem occurs while creating or
382       *                                    initializing the SSL context.
383       */
384      public SSLContext createSSLContext(final String protocol)
385             throws GeneralSecurityException
386      {
387        ensureNotNull(protocol);
388    
389        final SSLContext sslContext = SSLContext.getInstance(protocol);
390        sslContext.init(keyManagers, trustManagers, null);
391        return sslContext;
392      }
393    
394    
395    
396      /**
397       * Creates an initialized SSL context created with the configured key and
398       * trust managers.
399       *
400       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
401       *                   Architecture document, the set of supported protocols
402       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
403       *                   "SSLv2Hello".  It must not be {@code null}.
404       * @param  provider  The name of the provider to use for cryptographic
405       *                   operations.  It must not be {@code null}.
406       *
407       * @return  The created SSL context.
408       *
409       * @throws  GeneralSecurityException  If a problem occurs while creating or
410       *                                    initializing the SSL context.
411       */
412      public SSLContext createSSLContext(final String protocol,
413                                         final String provider)
414             throws GeneralSecurityException
415      {
416        ensureNotNull(protocol, provider);
417    
418        final SSLContext sslContext = SSLContext.getInstance(protocol, provider);
419        sslContext.init(keyManagers, trustManagers, null);
420        return sslContext;
421      }
422    
423    
424    
425      /**
426       * Creates an SSL socket factory using the configured key and trust manager
427       * providers.  It will use the protocol returned by the
428       * {@code getDefaultSSLProtocol} method and the JVM-default provider.
429       *
430       * @return  The created SSL socket factory.
431       *
432       * @throws  GeneralSecurityException  If a problem occurs while creating or
433       *                                    initializing the SSL socket factory.
434       */
435      public SSLSocketFactory createSSLSocketFactory()
436             throws GeneralSecurityException
437      {
438        return createSSLContext().getSocketFactory();
439      }
440    
441    
442    
443      /**
444       * Creates an SSL socket factory with the configured key and trust managers.
445       * It will use the default provider.
446       *
447       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
448       *                   Architecture document, the set of supported protocols
449       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
450       *                   "SSLv2Hello".  It must not be {@code null}.
451       *
452       * @return  The created SSL socket factory.
453       *
454       * @throws  GeneralSecurityException  If a problem occurs while creating or
455       *                                    initializing the SSL socket factory.
456       */
457      public SSLSocketFactory createSSLSocketFactory(final String protocol)
458             throws GeneralSecurityException
459      {
460        return createSSLContext(protocol).getSocketFactory();
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       * {@code 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       * {@code 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       * {@code 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          LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.<String>emptySet());
630        }
631        else
632        {
633          final HashSet<String> lowerProtocols =
634               new HashSet<String>(enabledSSLProtocols.size());
635          for (final String s : enabledSSLProtocols)
636          {
637            lowerProtocols.add(StaticUtils.toLowerCase(s));
638          }
639    
640          ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
641               new HashSet<String>(enabledSSLProtocols)));
642          LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
643               new HashSet<String>(lowerProtocols)));
644        }
645      }
646    
647    
648    
649      /**
650       * Updates the provided socket to apply the appropriate set of enabled SSL
651       * protocols.  This will only have any effect for sockets that are instances
652       * of {@code javax.net.ssl.SSLSocket}, but it is safe to call for any kind of
653       * {@code java.net.Socket}.  This should be called before attempting any
654       * communication over the socket, as
655       *
656       * @param  socket  The socket on which to apply the configured set of enabled
657       *                 SSL protocols.
658       *
659       * @throws  LDAPException  If {@code getEnabledSSLProtocols} returns a
660       *                         non-empty set but none of the values in that set
661       *                         are supported by
662       */
663      public static void applyEnabledSSLProtocols(final Socket socket)
664             throws LDAPException
665      {
666        if ((socket == null) || (!(socket instanceof SSLSocket)))
667        {
668          return;
669        }
670    
671        final Set<String> lowerEnabledProtocols = LOWER_ENABLED_SSL_PROTOCOLS.get();
672        if (lowerEnabledProtocols.isEmpty())
673        {
674          return;
675        }
676    
677        final SSLSocket sslSocket = (SSLSocket) socket;
678        final String[] supportedProtocols = sslSocket.getSupportedProtocols();
679    
680        final ArrayList<String> enabledList =
681             new ArrayList<String>(supportedProtocols.length);
682        for (final String supportedProtocol : supportedProtocols)
683        {
684          if (lowerEnabledProtocols.contains(
685               StaticUtils.toLowerCase(supportedProtocol)))
686          {
687            enabledList.add(supportedProtocol);
688          }
689        }
690    
691        if (enabledList.isEmpty())
692        {
693          final StringBuilder enabledBuffer = new StringBuilder();
694          final Iterator<String> enabledIterator =
695               ENABLED_SSL_PROTOCOLS.get().iterator();
696          while (enabledIterator.hasNext())
697          {
698            enabledBuffer.append('\'');
699            enabledBuffer.append(enabledIterator.next());
700            enabledBuffer.append('\'');
701    
702            if (enabledIterator.hasNext())
703            {
704              enabledBuffer.append(", ");
705            }
706          }
707    
708          final StringBuilder supportedBuffer = new StringBuilder();
709          for (int i=0; i < supportedProtocols.length; i++)
710          {
711            if (i > 0)
712            {
713              supportedBuffer.append(", ");
714            }
715    
716            supportedBuffer.append('\'');
717            supportedBuffer.append(supportedProtocols[i]);
718            supportedBuffer.append('\'');
719          }
720    
721          throw new LDAPException(ResultCode.CONNECT_ERROR,
722               ERR_NO_ENABLED_SSL_PROTOCOLS_AVAILABLE_FOR_SOCKET.get(
723                    enabledBuffer.toString(), supportedBuffer.toString(),
724                    PROPERTY_ENABLED_SSL_PROTOCOLS,
725                    SSLUtil.class.getName() + ".setEnabledSSLProtocols"));
726        }
727        else
728        {
729          final String[] enabledArray = new String[enabledList.size()];
730          sslSocket.setEnabledProtocols(enabledList.toArray(enabledArray));
731        }
732      }
733    
734    
735    
736      /**
737       * Configures SSL default settings for the LDAP SDK.  This method is
738       * non-private for purposes of easier test coverage.
739       */
740      static void configureSSLDefaults()
741      {
742        // See if there is a system property that specifies what the default SSL
743        // protocol should be.  If not, then try to dynamically determine it.
744        final String defaultPropValue =
745             System.getProperty(PROPERTY_DEFAULT_SSL_PROTOCOL);
746        if ((defaultPropValue != null) && (defaultPropValue.length() > 0))
747        {
748          DEFAULT_SSL_PROTOCOL.set(defaultPropValue);
749        }
750        else
751        {
752          // Ideally, we should be able to discover the SSL protocol that offers the
753          // best mix of security and compatibility.  Unfortunately, Java SE 5
754          // doesn't expose the methods necessary to allow us to do that, but if the
755          // running JVM is Java SE 6 or later, then we can use reflection to invoke
756          // those methods and make the appropriate determination.  If we see that
757          // TLSv1.1 and/or TLSv1.2 are available, then we'll add those to the set
758          // of default enabled protocols.
759          try
760          {
761            final Method getDefaultMethod =
762                 SSLContext.class.getMethod("getDefault");
763            final SSLContext defaultContext =
764                 (SSLContext) getDefaultMethod.invoke(null);
765    
766            final Method getSupportedParamsMethod =
767                 SSLContext.class.getMethod("getSupportedSSLParameters");
768            final Object paramsObj =
769                 getSupportedParamsMethod.invoke(defaultContext);
770    
771            final Class<?> sslParamsClass =
772                 Class.forName("javax.net.ssl.SSLParameters");
773            final Method getProtocolsMethod =
774                 sslParamsClass.getMethod("getProtocols");
775            final String[] supportedProtocols =
776                 (String[]) getProtocolsMethod.invoke(paramsObj);
777    
778            final HashSet<String> protocolMap =
779                 new HashSet<String>(Arrays.asList(supportedProtocols));
780            if (protocolMap.contains("TLSv1.2"))
781            {
782              DEFAULT_SSL_PROTOCOL.set("TLSv1.2");
783            }
784            else if (protocolMap.contains("TLSv1.1"))
785            {
786              DEFAULT_SSL_PROTOCOL.set("TLSv1.1");
787            }
788            else if (protocolMap.contains("TLSv1"))
789            {
790              DEFAULT_SSL_PROTOCOL.set("TLSv1");
791            }
792          }
793          catch (final Exception e)
794          {
795            Debug.debugException(e);
796          }
797        }
798    
799        // A set to use for the default set of enabled protocols.  Unless otherwise
800        // specified via system property, we'll always enable TLSv1.  We may enable
801        // other protocols based on the default protocol.  The default set of
802        // enabled protocols will not include SSLv3 even if the JVM might otherwise
803        // include it as a default enabled protocol because of known security
804        // problems with SSLv3.
805        final HashSet<String> enabledProtocols = new HashSet<String>(10);
806        enabledProtocols.add("TLSv1");
807        if (DEFAULT_SSL_PROTOCOL.get().equals("TLSv1.2"))
808        {
809          enabledProtocols.add("TLSv1.1");
810          enabledProtocols.add("TLSv1.2");
811        }
812        else if (DEFAULT_SSL_PROTOCOL.get().equals("TLSv1.1"))
813        {
814          enabledProtocols.add("TLSv1.1");
815        }
816    
817        // If there is a system property that specifies which enabled SSL protocols
818        // to use, then it will override the defaults.
819        final String enabledPropValue =
820             System.getProperty(PROPERTY_ENABLED_SSL_PROTOCOLS);
821        if ((enabledPropValue != null) && (enabledPropValue.length() > 0))
822        {
823          enabledProtocols.clear();
824    
825          final StringTokenizer tokenizer = new StringTokenizer(enabledPropValue,
826               ", ", false);
827          while (tokenizer.hasMoreTokens())
828          {
829            final String token = tokenizer.nextToken();
830            if (token.length() > 0)
831            {
832              enabledProtocols.add(token);
833            }
834          }
835        }
836    
837        // Get all-lowercase representations of the enabled protocols for more
838        // efficient comparisons.
839        final HashSet<String> lowerEnabledProtocols =
840             new HashSet<String>(enabledProtocols.size());
841        for (final String s : enabledProtocols)
842        {
843          lowerEnabledProtocols.add(StaticUtils.toLowerCase(s));
844        }
845    
846        ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(enabledProtocols));
847        LOWER_ENABLED_SSL_PROTOCOLS.set(Collections.unmodifiableSet(
848             lowerEnabledProtocols));
849      }
850    }