I'm working on a SpringBoot application which needs to make RESTful calls to an internal API that uses a self-signed certificate.
I'm only having this issue in DEV and QA since in UAT and PROD they're using a properly signed certificate.
I'm developing on a Windows 10 machine and using Java 8.
I've tried the below with no luck:
Added certificate to Windows trusted certificates
- I downloaded the certificate from Chrome (in the address bar where it shows that the certificate is not valid).
- Then, in Windows Explorer, I right-clicked the certificate file and selected Install Certificate and followed the wizard.
Adding code to ignore SSL verification
I called the SSLUtils.buildRestTemplate method when creating a RestTemplate.
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) && (sslBypassConfiguration.getWhitelistedHostnames().size() > 0))
{
final StringBuilder sb = new StringBuilder("Hostnames Being White-Listed:\n");
// Loop over all white-listed hostnames and log them.
for (Entry<String, String> 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);
}
}
Possible Idea
Is there an application that could accept HTTP connections and route them to an externally hosted HTTPS endpoint?
This application would have to ignore any certificate issues.