View Javadoc
1   package net.trajano.commons.testing;
2   
3   import static javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier;
4   import static javax.net.ssl.HttpsURLConnection.getDefaultSSLSocketFactory;
5   import static javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier;
6   import static javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory;
7   
8   import java.io.IOException;
9   import java.net.URL;
10  import java.security.GeneralSecurityException;
11  import java.util.logging.Level;
12  import java.util.logging.Logger;
13  
14  import javax.net.ssl.HostnameVerifier;
15  import javax.net.ssl.SSLContext;
16  import javax.net.ssl.SSLSocketFactory;
17  import javax.net.ssl.TrustManager;
18  import javax.net.ssl.X509TrustManager;
19  
20  import net.trajano.commons.testing.internal.NullHostnameVerifier;
21  import net.trajano.commons.testing.internal.NullX509TrustManager;
22  
23  /**
24   * Disables SSL certificate checks. Primarily used for doing integration testing
25   * against self-signed servers.
26   */
27  public final class DisableSslCertificateCheckUtil {
28      /**
29       * Flag to indicate that certificate checks are disabled. If this is true,
30       * then the process to disable the checks are not executed again.
31       */
32      private static boolean disabled;
33  
34      /**
35       * Logger.
36       */
37      private static final Logger LOG = Logger
38              .getLogger(DisableSslCertificateCheckUtil.class.getName(),
39                      "META-INF.Messages");
40  
41      /**
42       * Null host name verifier. Made it a constant to prevent new
43       * instantiations. Made public so the instance can be retrieved directly.
44       */
45      public static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new NullHostnameVerifier();
46  
47      /**
48       * Null trust manager. Made it a constant to prevent new instantiations.
49       * Made public so the instance can be retrieved directly.
50       */
51      public static final X509TrustManager NULL_TRUST_MANAGER = new NullX509TrustManager();
52  
53      /**
54       * Original hostname verifier, set by {{@link #disableChecks()}.
55       */
56      private static HostnameVerifier originalHostnameVerifier;
57  
58      /**
59       * Original SSL Socket factory, set by {{@link #disableChecks()}.
60       */
61      private static SSLSocketFactory originalSslSocketFactory;
62  
63      /**
64       * <p>
65       * Constructs an unsecure SSL context. This SSL context is configured with a
66       * {@link #NULL_TRUST_MANAGER}. There is no guarantee that the
67       * {@link SSLContext} is thread-safe so new ones have to get created in
68       * order to be safe.
69       * </p>
70       * <p>
71       * The <code>TLSv1</code> is guaranteed to be present according to the
72       * {@link SSLContext} javadoc. The {@link SSLContext#getInstance(String)}
73       * method is used rather than {@link SSLContext#getDefault()} as the default
74       * context would have already been initialized therefore it would not allow
75       * us to execute
76       * {@link SSLContext#init(javax.net.ssl.KeyManager[], TrustManager[], java.security.SecureRandom)}
77       * .
78       * </p>
79       *
80       * @return an unsecure SSL context.
81       * @throws GeneralSecurityException
82       */
83      public static SSLContext buildUnsecureSslContext()
84              throws GeneralSecurityException {
85          final SSLContext context = SSLContext.getInstance("TLSv1");
86          final TrustManager[] trustManagerArray = { NULL_TRUST_MANAGER };
87          context.init(null, trustManagerArray, null);
88          return context;
89      }
90  
91      /**
92       * Disable trust checks for SSL connections. Saves the present ones if it is
93       * not the disabled ones.
94       *
95       * @throws GeneralSecurityException
96       *             thrown when there is a problem disabling the SSL. Shouldn't
97       *             happen unless there is something wrong with the Java
98       *             implementation.
99       */
100     public static void disableChecks() throws GeneralSecurityException {
101         if (disabled) {
102             return;
103         }
104         try {
105             new URL("https", "0", "/").getContent();
106         } catch (final IOException e) {
107             // This invocation will always fail, but it will register the
108             // default SSL provider to the URL class.
109             LOG.log(Level.FINEST,
110                     "DisableSSLCertificateCheckUtil.disableCertificateCheck", e);
111         }
112         originalSslSocketFactory = getDefaultSSLSocketFactory();
113         originalHostnameVerifier = getDefaultHostnameVerifier();
114         final SSLContext context = buildUnsecureSslContext();
115         setDefaultSSLSocketFactory(context.getSocketFactory());
116         setDefaultHostnameVerifier(NULL_HOSTNAME_VERIFIER);
117         disabled = true;
118     }
119 
120     /**
121      * This will re-enable the SSL checks after it was disabled by
122      * {@link #disableChecks()}.
123      */
124     public static void reenableChecks() {
125         if (!disabled) {
126             return;
127         }
128         setDefaultSSLSocketFactory(originalSslSocketFactory);
129         setDefaultHostnameVerifier(originalHostnameVerifier);
130         disabled = false;
131     }
132 
133     /**
134      * Prevent instantiation of utility class.
135      */
136     private DisableSslCertificateCheckUtil() {
137     }
138 }