001    /*
002     * Copyright 2008-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 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.security.GeneralSecurityException;
027    import java.util.Arrays;
028    import java.util.HashSet;
029    import java.util.concurrent.atomic.AtomicReference;
030    import javax.net.ssl.KeyManager;
031    import javax.net.ssl.SSLContext;
032    import javax.net.ssl.SSLSocketFactory;
033    import javax.net.ssl.SSLServerSocketFactory;
034    import javax.net.ssl.TrustManager;
035    
036    import com.unboundid.util.Debug;
037    import com.unboundid.util.ThreadSafety;
038    import com.unboundid.util.ThreadSafetyLevel;
039    
040    import static com.unboundid.util.Validator.*;
041    
042    
043    
044    /**
045     * This class provides a simple interface for creating {@code SSLContext} and
046     * {@code SSLSocketFactory} instances, which may be used to create SSL-based
047     * connections, or secure existing connections with StartTLS.
048     * <BR><BR>
049     * <H2>Example 1</H2>
050     * The following example demonstrates the use of the SSL helper to create an
051     * SSL-based LDAP connection that will blindly trust any certificate that the
052     * server presents:
053     * <PRE>
054     *   SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
055     *
056     *   LDAPConnection connection =
057     *        new LDAPConnection(sslUtil.createSSLSocketFactory());
058     *   connection.connect("server.example.com", 636);
059     * </PRE>
060     * <BR>
061     * <H2>Example 2</H2>
062     * The following example demonstrates the use of the SSL helper to create a
063     * non-secure LDAP connection and then use the StartTLS extended operation to
064     * secure it.  It will use a trust store to determine whether to trust the
065     * server certificate.
066     * <PRE>
067     *   LDAPConnection connection = new LDAPConnection();
068     *   connection.connect("server.example.com", 389);
069     *
070     *   String trustStoreFile  = "/path/to/trust/store/file";
071     *   SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStoreFile));
072     *
073     *   ExtendedResult extendedResult = connection.processExtendedOperation(
074     *        new StartTLSExtendedRequest(sslUtil.createSSLContext()));
075     * </PRE>
076     */
077    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
078    public final class SSLUtil
079    {
080      /**
081       * The default protocol string that will be used to create SSL contexts when
082       * no explicit protocol is specified.
083       */
084      private static final AtomicReference<String> DEFAULT_SSL_PROTOCOL =
085           new AtomicReference<String>("TLSv1");
086    
087      static
088      {
089        // Ideally, we should be able to discover the SSL protocol that offers the
090        // best mix of security and compatibility.  Unfortunately, Java SE 5 doesn't
091        // expose the methods necessary to allow us to do that, but if the running
092        // JVM is Java SE 6 or later, then we can use reflection to invoke those
093        // methods and make the appropriate determination.
094    
095        try
096        {
097          final Method getDefaultMethod =
098               SSLContext.class.getMethod("getDefault");
099          final SSLContext defaultContext =
100               (SSLContext) getDefaultMethod.invoke(null);
101    
102          final Method getSupportedParamsMethod =
103               SSLContext.class.getMethod("getSupportedSSLParameters");
104          final Object paramsObj = getSupportedParamsMethod.invoke(defaultContext);
105    
106          final Class<?> sslParamsClass =
107               Class.forName("javax.net.ssl.SSLParameters");
108          final Method getProtocolsMethod =
109               sslParamsClass.getMethod("getProtocols");
110          final String[] supportedProtocols =
111               (String[]) getProtocolsMethod.invoke(paramsObj);
112    
113          final HashSet<String> protocolMap =
114               new HashSet<String>(Arrays.asList(supportedProtocols));
115          if (protocolMap.contains("TLSv1.2"))
116          {
117            DEFAULT_SSL_PROTOCOL.set("TLSv1.2");
118          }
119          else if (protocolMap.contains("TLSv1.1"))
120          {
121            DEFAULT_SSL_PROTOCOL.set("TLSv1.1");
122          }
123          else if (protocolMap.contains("TLSv1"))
124          {
125            DEFAULT_SSL_PROTOCOL.set("TLSv1");
126          }
127        }
128        catch (final Exception e)
129        {
130          Debug.debugException(e);
131        }
132      }
133    
134    
135    
136      // The set of key managers to be used.
137      private final KeyManager[] keyManagers;
138    
139      // The set of trust managers to be used.
140      private final TrustManager[] trustManagers;
141    
142    
143    
144      /**
145       * Creates a new SSLUtil instance that will not have a custom key manager or
146       * trust manager.  It will not be able to provide a certificate to the server
147       * if one is requested, and it will only trust certificates signed by a
148       * predefined set of authorities.
149       */
150      public SSLUtil()
151      {
152        keyManagers   = null;
153        trustManagers = null;
154      }
155    
156    
157    
158      /**
159       * Creates a new SSLUtil instance that will use the provided trust manager to
160       * determine whether to trust server certificates presented to the client.
161       * It will not be able to provide a certificate to the server if one is
162       * requested.
163       *
164       * @param  trustManager  The trust manager to use to determine whether to
165       *                       trust server certificates presented to the client.
166       *                       It may be {@code null} if the default set of trust
167       *                       managers should be used.
168       */
169      public SSLUtil(final TrustManager trustManager)
170      {
171        keyManagers = null;
172    
173        if (trustManager == null)
174        {
175          trustManagers = null;
176        }
177        else
178        {
179          trustManagers = new TrustManager[] { trustManager };
180        }
181      }
182    
183    
184    
185      /**
186       * Creates a new SSLUtil instance that will use the provided trust managers
187       * to determine whether to trust server certificates presented to the client.
188       * It will not be able to provide a certificate to the server if one is
189       * requested.
190       *
191       * @param  trustManagers  The set of trust managers to use to determine
192       *                        whether to trust server certificates presented to
193       *                        the client.  It may be {@code null} or empty if the
194       *                        default set of trust managers should be used.
195       */
196      public SSLUtil(final TrustManager[] trustManagers)
197      {
198        keyManagers = null;
199    
200        if ((trustManagers == null) || (trustManagers.length == 0))
201        {
202          this.trustManagers = null;
203        }
204        else
205        {
206          this.trustManagers = trustManagers;
207        }
208      }
209    
210    
211    
212      /**
213       * Creates a new SSLUtil instance that will use the provided key manager to
214       * obtain certificates to present to the server, and the provided trust
215       * manager to determine whether to trust server certificates presented to the
216       * client.
217       *
218       * @param  keyManager    The key manager to use to obtain certificates to
219       *                       present to the server if requested.  It may be
220       *                       {@code null} if no client certificates will be
221       *                       required or should be provided.
222       * @param  trustManager  The trust manager to use to determine whether to
223       *                       trust server certificates presented to the client.
224       *                       It may be {@code null} if the default set of trust
225       *                       managers should be used.
226       */
227      public SSLUtil(final KeyManager keyManager, final TrustManager trustManager)
228      {
229        if (keyManager == null)
230        {
231          keyManagers = null;
232        }
233        else
234        {
235          keyManagers = new KeyManager[] { keyManager };
236        }
237    
238        if (trustManager == null)
239        {
240          trustManagers = null;
241        }
242        else
243        {
244          trustManagers = new TrustManager[] { trustManager };
245        }
246      }
247    
248    
249    
250      /**
251       * Creates a new SSLUtil instance that will use the provided key managers to
252       * obtain certificates to present to the server, and the provided trust
253       * managers to determine whether to trust server certificates presented to the
254       * client.
255       *
256       * @param  keyManagers    The set of key managers to use to obtain
257       *                        certificates to present to the server if requested.
258       *                        It may be {@code null} or empty if no client
259       *                        certificates will be required or should be provided.
260       * @param  trustManagers  The set of trust managers to use to determine
261       *                        whether to trust server certificates presented to
262       *                        the client.  It may be {@code null} or empty if the
263       *                        default set of trust managers should be used.
264       */
265      public SSLUtil(final KeyManager[] keyManagers,
266                     final TrustManager[] trustManagers)
267      {
268        if ((keyManagers == null) || (keyManagers.length == 0))
269        {
270          this.keyManagers = null;
271        }
272        else
273        {
274          this.keyManagers = keyManagers;
275        }
276    
277        if ((trustManagers == null) || (trustManagers.length == 0))
278        {
279          this.trustManagers = null;
280        }
281        else
282        {
283          this.trustManagers = trustManagers;
284        }
285      }
286    
287    
288    
289      /**
290       * Retrieves the set of key managers configured for use by this class, if any.
291       *
292       * @return  The set of key managers configured for use by this class, or
293       *          {@code null} if none were provided.
294       */
295      public KeyManager[] getKeyManagers()
296      {
297        return keyManagers;
298      }
299    
300    
301    
302      /**
303       * Retrieves the set of trust managers configured for use by this class, if
304       * any.
305       *
306       * @return  The set of trust managers configured for use by this class, or
307       *          {@code null} if none were provided.
308       */
309      public TrustManager[] getTrustManagers()
310      {
311        return trustManagers;
312      }
313    
314    
315    
316      /**
317       * Creates an initialized SSL context created with the configured key and
318       * trust managers.  It will use the protocol returned by the
319       * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
320       *
321       * @return  The created SSL context.
322       *
323       * @throws  GeneralSecurityException  If a problem occurs while creating or
324       *                                    initializing the SSL context.
325       */
326      public SSLContext createSSLContext()
327             throws GeneralSecurityException
328      {
329        return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
330      }
331    
332    
333    
334      /**
335       * Creates an initialized SSL context created with the configured key and
336       * trust managers.  It will use the default provider.
337       *
338       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
339       *                   Architecture document, the set of supported protocols
340       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
341       *                   "SSLv2Hello".  It must not be {@code null}.
342       *
343       * @return  The created SSL context.
344       *
345       * @throws  GeneralSecurityException  If a problem occurs while creating or
346       *                                    initializing the SSL context.
347       */
348      public SSLContext createSSLContext(final String protocol)
349             throws GeneralSecurityException
350      {
351        ensureNotNull(protocol);
352    
353        final SSLContext sslContext = SSLContext.getInstance(protocol);
354        sslContext.init(keyManagers, trustManagers, null);
355        return sslContext;
356      }
357    
358    
359    
360      /**
361       * Creates an initialized SSL context created with the configured key and
362       * trust managers.
363       *
364       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
365       *                   Architecture document, the set of supported protocols
366       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
367       *                   "SSLv2Hello".  It must not be {@code null}.
368       * @param  provider  The name of the provider to use for cryptographic
369       *                   operations.  It must not be {@code null}.
370       *
371       * @return  The created SSL context.
372       *
373       * @throws  GeneralSecurityException  If a problem occurs while creating or
374       *                                    initializing the SSL context.
375       */
376      public SSLContext createSSLContext(final String protocol,
377                                         final String provider)
378             throws GeneralSecurityException
379      {
380        ensureNotNull(protocol, provider);
381    
382        final SSLContext sslContext = SSLContext.getInstance(protocol, provider);
383        sslContext.init(keyManagers, trustManagers, null);
384        return sslContext;
385      }
386    
387    
388    
389      /**
390       * Creates an SSL socket factory using the configured key and trust manager
391       * providers.  It will use the protocol returned by the
392       * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
393       *
394       * @return  The created SSL socket factory.
395       *
396       * @throws  GeneralSecurityException  If a problem occurs while creating or
397       *                                    initializing the SSL socket factory.
398       */
399      public SSLSocketFactory createSSLSocketFactory()
400             throws GeneralSecurityException
401      {
402        return createSSLContext().getSocketFactory();
403      }
404    
405    
406    
407      /**
408       * Creates an SSL socket factory with the configured key and trust managers.
409       * It will use the default provider.
410       *
411       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
412       *                   Architecture document, the set of supported protocols
413       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
414       *                   "SSLv2Hello".  It must not be {@code null}.
415       *
416       * @return  The created SSL socket factory.
417       *
418       * @throws  GeneralSecurityException  If a problem occurs while creating or
419       *                                    initializing the SSL socket factory.
420       */
421      public SSLSocketFactory createSSLSocketFactory(final String protocol)
422             throws GeneralSecurityException
423      {
424        return createSSLContext(protocol).getSocketFactory();
425      }
426    
427    
428    
429      /**
430       * Creates an SSL socket factory with the configured key and trust managers.
431       *
432       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
433       *                   Architecture document, the set of supported protocols
434       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
435       *                   "SSLv2Hello".  It must not be {@code null}.
436       * @param  provider  The name of the provider to use for cryptographic
437       *                   operations.  It must not be {@code null}.
438       *
439       * @return  The created SSL socket factory.
440       *
441       * @throws  GeneralSecurityException  If a problem occurs while creating or
442       *                                    initializing the SSL socket factory.
443       */
444      public SSLSocketFactory createSSLSocketFactory(final String protocol,
445                                                     final String provider)
446             throws GeneralSecurityException
447      {
448        return createSSLContext(protocol, provider).getSocketFactory();
449      }
450    
451    
452    
453      /**
454       * Creates an SSL server socket factory using the configured key and trust
455       * manager providers.  It will use the protocol returned by the
456       * {@link #getDefaultSSLProtocol} method and the JVM-default provider.
457       *
458       * @return  The created SSL server socket factory.
459       *
460       * @throws  GeneralSecurityException  If a problem occurs while creating or
461       *                                    initializing the SSL server socket
462       *                                    factory.
463       */
464      public SSLServerSocketFactory createSSLServerSocketFactory()
465             throws GeneralSecurityException
466      {
467        return createSSLContext().getServerSocketFactory();
468      }
469    
470    
471    
472      /**
473       * Creates an SSL server socket factory using the configured key and trust
474       * manager providers.  It will use the JVM-default provider.
475       *
476       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
477       *                   Architecture document, the set of supported protocols
478       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
479       *                   "SSLv2Hello".  It must not be {@code null}.
480       *
481       * @return  The created SSL server socket factory.
482       *
483       * @throws  GeneralSecurityException  If a problem occurs while creating or
484       *                                    initializing the SSL server socket
485       *                                    factory.
486       */
487      public SSLServerSocketFactory createSSLServerSocketFactory(
488                                         final String protocol)
489             throws GeneralSecurityException
490      {
491        return createSSLContext(protocol).getServerSocketFactory();
492      }
493    
494    
495    
496      /**
497       * Creates an SSL server socket factory using the configured key and trust
498       * manager providers.
499       *
500       * @param  protocol  The protocol to use.  As per the Java SE 6 Cryptography
501       *                   Architecture document, the set of supported protocols
502       *                   should include at least "SSLv3", "TLSv1", "TLSv1.1", and
503       *                   "SSLv2Hello".  It must not be {@code null}.
504       * @param  provider  The name of the provider to use for cryptographic
505       *                   operations.  It must not be {@code null}.
506       *
507       * @return  The created SSL server socket factory.
508       *
509       * @throws  GeneralSecurityException  If a problem occurs while creating or
510       *                                    initializing the SSL server socket
511       *                                    factory.
512       */
513      public SSLServerSocketFactory createSSLServerSocketFactory(
514                                         final String protocol,
515                                         final String provider)
516             throws GeneralSecurityException
517      {
518        return createSSLContext(protocol, provider).getServerSocketFactory();
519      }
520    
521    
522    
523      /**
524       * Retrieves the SSL protocol string that will be used by calls to
525       * {@link #createSSLContext()} that do not explicitly specify which protocol
526       * to use.
527       *
528       * @return  The SSL protocol string that will be used by calls to create an
529       *          SSL context that do not explicitly specify which protocol to use.
530       */
531      public static String getDefaultSSLProtocol()
532      {
533        return DEFAULT_SSL_PROTOCOL.get();
534      }
535    
536    
537    
538      /**
539       * Specifies the SSL protocol string that will be used by calls to
540       * {@link #createSSLContext()} that do not explicitly specify which protocol
541       * to use.
542       *
543       * @param  defaultSSLProtocol  The SSL protocol string that will be used by
544       *                             calls to create an SSL context that do not
545       *                             explicitly specify which protocol to use.  It
546       *                             must not be {@code null}.
547       */
548      public static void setDefaultSSLProtocol(final String defaultSSLProtocol)
549      {
550        ensureNotNull(defaultSSLProtocol);
551    
552        DEFAULT_SSL_PROTOCOL.set(defaultSSLProtocol);
553      }
554    }