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:
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);
}
}
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.
The simplest configuration that turn off all ssl verification is:
with a factory
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
trust all TLS with the org.apache.http.conn.ssl.NoopHostnameVerifier
class from
`<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>`
Create a new client
SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(null, (chain, authType) -> true) .build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, new NoopHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
Create a new resttemplate with this new client
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
resuming:
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(null, (chain, authType) -> true) .build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, new NoopHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);