title: Ignoring self-signed certificates in SpringBoot using RestTemplate
body: <p>I'm working on a SpringBoot application which needs to make RESTful calls to an internal API that uses a self-signed certificate.</p>
  
  <p>I'm only having this issue in DEV and QA since in UAT and PROD they're using a properly signed certificate.</p>
  
  <p>I'm developing on a Windows 10 machine and using Java 8.</p>
  
  <hr>
  
  <p><strong>I've tried the below with no luck:</strong></p>
  
  <h2>Added certificate to Windows trusted certificates</h2>
  
  <ol>
  <li>I downloaded the certificate from Chrome (in the address bar where
  it shows that the certificate is not valid).</li>
  <li>Then, in Windows Explorer, I right-clicked the certificate file and selected <strong>Install Certificate</strong> and followed the wizard.</li>
  </ol>
  
  <h2>Adding code to ignore SSL verification</h2>
  
  <p>I called the <strong>SSLUtils.buildRestTemplate</strong> method when creating a <strong>RestTemplate</strong>.</p>
  
  <pre><code>package com.company.project.utils.ssl;
  
  import java.security.KeyManagementException;
  import java.security.KeyStoreException;
  import java.security.NoSuchAlgorithmException;
  import java.util.Map.Entry;
  
  import javax.net.ssl.HostnameVerifier;
  import javax.net.ssl.HttpsURLConnection;
  import javax.net.ssl.SSLContext;
  import javax.net.ssl.TrustManager;
  import javax.net.ssl.X509TrustManager;
  
  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  import org.apache.http.impl.client.HttpClientBuilder;
  import org.apache.http.impl.client.HttpClients;
  import org.apache.http.ssl.TrustStrategy;
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
  import org.springframework.web.client.RestTemplate;
  
  import com.company.project.beans.ssl.SslBypassConfiguration;
  
  /**
   * This class contains several methods for manipulating SSL certificate verification.
   */
  public class SSLUtils
  {
  
      /* PRIVATE CONSTANTS */
      private static Logger logger = LogManager.getLogger(SSLUtils.class);
  
      /* PRIVATE VARIABLES */
      private static HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
  
      /* PUBLIC METHODS */
      /**
       * This method will set custom SSL certificate verification which will
       * only forgo SSL certificate verification for white-listed hostnames.
       * 
       * @param sslBypassConfiguration
       * The {@link SslBypassConfiguration} that contains the details needed.
       * 
       * @return
       * The boolean flag to denote if the operation was successful.
       * 
       * @throws NoSuchAlgorithmException
       * If no Provider supports aSSLContextSpi implementation for the
       * specified protocol.
       * @throws KeyManagementException
       * If the initialization fails.
       */
      public static boolean setCustomSslChecking(final SslBypassConfiguration sslBypassConfiguration)
              throws NoSuchAlgorithmException, KeyManagementException
      {
          // If the SSL bypass is enabled, then keep going.
          if (sslBypassConfiguration.isSslVerificationBypassEnabled())
          {
              // If there are some hostnames to white-list, then keep going.
              if ((sslBypassConfiguration.getWhitelistedHostnames() != null) &amp;&amp; (sslBypassConfiguration.getWhitelistedHostnames().size() &gt; 0))
              {
                  final StringBuilder sb = new StringBuilder("Hostnames Being White-Listed:\n");
  
                  // Loop over all white-listed hostnames and log them.
                  for (Entry&lt;String, String&gt; whitelistedHostname : sslBypassConfiguration.getWhitelistedHostnames().entrySet())
                  {
                      sb.append(whitelistedHostname.getKey())
                      .append(" (")
                      .append(whitelistedHostname.getValue())
                      .append(")");
                  }
  
                  logger.warn(sb.toString());
              }
              else
              {
                  logger.warn("SSL certificate verification bypass is enabled, but no white-listed hostnames have been specified.");
              }
  
              // Create the hostname verifier to be used.
              final WhitelistHostnameVerifier whitelistHostnameVerifier = new WhitelistHostnameVerifier(sslBypassConfiguration);
  
              // Create the trust manager to be used.
              final X509TrustManager trustManager = new TrustingX509TrustManager();
  
              // Assign the custom hostname verifier and trust manager.
              SSLUtils.setCustomSslChecking(whitelistHostnameVerifier, trustManager);
  
              return true;
          }
  
          return false;
      }
  
      /**
       * This method will set custom SSL certificate verification.
       * 
       * @param hostnameVerifier
       * The {@link javax.net.ssl.HostnameVerifier} that will be used to verify hostnames.
       * @param trustManager
       * The {@link X509TrustManager} that will be used to verify certificates.
       * 
       * @throws NoSuchAlgorithmException
       * If no Provider supports aSSLContextSpi implementation for the specified protocol.
       * @throws KeyManagementException
       * If the initialization fails.
       */
      public static void setCustomSslChecking(
              final javax.net.ssl.HostnameVerifier hostnameVerifier,
              final X509TrustManager trustManager)
              throws NoSuchAlgorithmException, KeyManagementException
      {
          // Get an instance of the SSLContent.
          final SSLContext sslContent = SSLContext.getInstance("SSL"); // TLS
  
          // Set the state using the specified TrustManager.
          sslContent.init(null, new TrustManager[] {trustManager}, null);
  
          // Set the derived SSL socket factory.
          HttpsURLConnection.setDefaultSSLSocketFactory(sslContent.getSocketFactory());
  
          // Define the default hostname verifier.
          javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
      }
  
      /**
       * This method will set the default SSL certificate verification.
       * 
       * @throws NoSuchAlgorithmException
       * If no Provider supports aSSLContextSpi implementation for the specified protocol.
       * @throws KeyManagementException
       * If the initialization fails.
       */
      public static void setDefaultSslChecking()
              throws NoSuchAlgorithmException, KeyManagementException
      {
          // Get an instance of the SSLContent.
          final SSLContext sslContent = SSLContext.getInstance("SSL"); // TLS
  
          // Return it to the initial state (discovered by reflection, now hardcoded).
          sslContent.init(null, null, null);
  
          // Set the default SSL socket factory.
          HttpsURLConnection.setDefaultSSLSocketFactory(sslContent.getSocketFactory());
  
          // Define the default hostname verifier.
          javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(SSLUtils.defaultHostnameVerifier);
      }
  
      /**
       * This method will build a new {@link RestTemplate}.
       * 
       * @param sslBypassConfiguration
       * The {@link SslBypassConfiguration}.
       * 
       * @return
       * The {@link RestTemplate}.
       * 
       * @throws KeyManagementException
       * @throws NoSuchAlgorithmException
       * @throws KeyStoreException
       */
      public static RestTemplate buildRestTemplate(final SslBypassConfiguration sslBypassConfiguration)
              throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException
      {
          if ((sslBypassConfiguration == null) || (!sslBypassConfiguration.isSslVerificationBypassEnabled()))
          {
              return new RestTemplate();
          }
  
          final TrustStrategy acceptingTrustStrategy = new TrustStrategy()
          {
              @Override
              public boolean isTrusted(final java.security.cert.X509Certificate[] chain, final String authType)
                      throws java.security.cert.CertificateException
              {
                  return true;
              }
          };
  
          final HttpClientBuilder httpClientBuilder = HttpClients.custom();
  
  
          final SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
          final SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
  
          httpClientBuilder.setSSLSocketFactory(csf);
          httpClientBuilder.setSSLHostnameVerifier(new WhitelistHostnameVerifier(sslBypassConfiguration));
  
          final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  
          requestFactory.setHttpClient(httpClientBuilder.build());
  
          return new RestTemplate(requestFactory);
      }
  
  }
  </code></pre>
  
  <h2>Possible Idea</h2>
  
  <p>Is there an application that could accept HTTP connections and route them to an externally hosted HTTPS endpoint?</p>
  
  <p>This application would have to ignore any certificate issues.</p>