Reputation: 790
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.
Upvotes: 1
Views: 13776
Reputation: 616
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);
Upvotes: 1
Reputation: 11
RestTemplate跳过https请求证书验证问题:
@Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
// SSLSocketFactory用于创建 SSLSockets
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
// 返回的密码套件可用于SSL连接启用的名字
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port,
final boolean autoClose) throws IOException {
Socket underlyingSocket = null;
try {
underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
} catch (java.io.IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
Socket underlyingSocket = null;
try {
underlyingSocket = delegate.createSocket(host, port);
} catch (java.io.IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
Socket underlyingSocket = null;
try {
underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
} catch (java.io.IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
Socket underlyingSocket = null;
try {
underlyingSocket = delegate.createSocket(host, port);
} catch (java.io.IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
Socket underlyingSocket = null;
try {
underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
} catch (java.io.IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
return socket;
}
}
Upvotes: 1
Reputation: 790
I found a solution to ignoring SSL verification for RestTemplates. I can't take the credit though, I found the answer on another question: other answer
Here's my code if anyone else can use it.
package com.company.project.utils.ssl;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
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.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import com.company.project.beans.ssl.SslBypassConfiguration;
/**
* This class contains several methods for manipulating SSL certificate verification.
*
* @author Matthew Weiler
*/
public class SSLUtils
{
/* PRIVATE CONSTANTS */
private static Logger logger = LogManager.getLogger(SSLUtils.class);
/* PUBLIC CONSTANTS */
public static final HostnameVerifier DEFAULT_HOSTNAMEVERIFIER = HttpsURLConnection.getDefaultHostnameVerifier();
/* PUBLIC METHODS */
/**
* This method will build a {@link RestTemplate}.
*
* @param sslBypassConfiguration
* The {@link SslBypassConfiguration}.
* @param username
* The username to be used when establishing a connection.
* @param password
* The password to be used when establishing a connection.
*
* @return
* The {@link RestTemplate}.
*
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
public static RestTemplate buildRestTemplate(final SslBypassConfiguration sslBypassConfiguration, final String username, final String password)
throws KeyManagementException, NoSuchAlgorithmException
{
final RestTemplate restTemplate = new RestTemplate(SSLUtils.createSecureTransport(sslBypassConfiguration, username, password));
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
return restTemplate;
}
/* PROTECTED METHODS */
/**
* This method will build a {@link ClientHttpRequestFactory}.
*
* @param sslBypassConfiguration
* The {@link SslBypassConfiguration}.
* @param username
* The username to be used when establishing a connection.
* @param password
* The password to be used when establishing a connection.
*
* @return
* The {@link ClientHttpRequestFactory}.
*
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
protected static ClientHttpRequestFactory createSecureTransport(final SslBypassConfiguration sslBypassConfiguration, final String username, final String password) throws KeyManagementException, NoSuchAlgorithmException
{
final HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
if (sslBypassConfiguration.isSslVerificationBypassEnabled())
{
httpClientBuilder.setSSLHostnameVerifier(new WhitelistHostnameVerifier(sslBypassConfiguration));
}
httpClientBuilder.setSSLContext(SSLUtils.createContext(sslBypassConfiguration));
// If credentials are supplied, apply them to the http-client-builder.
if (((username != null) && (username.trim().length() > 0)) || ((password != null) && (password.trim().length() > 0)))
{
final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM), credentials);
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
}
/**
* This method will build a {@link SSLContext}.
*
* @param sslBypassConfiguration
* The {@link SslBypassConfiguration}.
*
* @return
* The {@link SSLContext}.
*
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
protected static SSLContext createContext(final SslBypassConfiguration sslBypassConfiguration)
throws NoSuchAlgorithmException, KeyManagementException
{
final SSLContext sslContext = SSLContext.getInstance("SSL");
if (sslBypassConfiguration.isSslVerificationBypassEnabled())
{
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
}};
sslContext.init(null, trustAllCerts, null);
}
else
{
sslContext.init(null, null, null);
}
SSLContext.setDefault(sslContext);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
if (sslBypassConfiguration.isSslVerificationBypassEnabled())
{
HttpsURLConnection.setDefaultHostnameVerifier(new WhitelistHostnameVerifier(sslBypassConfiguration));
}
else
{
HttpsURLConnection.setDefaultHostnameVerifier(SSLUtils.DEFAULT_HOSTNAMEVERIFIER);
}
return sslContext;
}
}
package com.company.project.utils.ssl;
import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.company.project.beans.ssl.SslBypassConfiguration;
/**
* This {@link HostnameVerifier} will ignore the SSL validation for hostnames that are on the white-list.
*
* @author Matthew Weiler
*/
public class WhitelistHostnameVerifier implements HostnameVerifier
{
/* PRIVATE CONSTANTS */
private static Logger logger = LogManager.getLogger(WhitelistHostnameVerifier.class);
/* PRIVATE VARIABLES */
private final SslBypassConfiguration sslBypassConfiguration;
/* CONSTRUCTORS */
/**
* This will create a new {@link WhitelistHostnameVerifier}.
*
* @param sslBypassConfiguration
* The {@link SslBypassConfiguration} to be used.
*/
public WhitelistHostnameVerifier(
final SslBypassConfiguration sslBypassConfiguration)
{
this.sslBypassConfiguration = sslBypassConfiguration;
}
/* PUBLIC METHODS */
@Override
public boolean verify(final String host, final SSLSession session)
{
if (this.sslBypassConfiguration.isSslVerificationBypassEnabled())
{
if ((this.sslBypassConfiguration.getWhitelistedHostnames() != null) && (this.sslBypassConfiguration.getWhitelistedHostnames().size() > 0))
{
for (Entry<String, String> whitelistEntry : this.sslBypassConfiguration.getWhitelistedHostnames().entrySet())
{
if (whitelistEntry.getValue().equals(host))
{
logger.info("Not performing validation on SSL connection for: " + whitelistEntry.getKey() + " (" + whitelistEntry.getValue() + ")");
return true;
}
}
}
}
// important: use default verifier for all other hosts
return SSLUtils.DEFAULT_HOSTNAMEVERIFIER.verify(host, session);
}
/**
* This method will get the {@link SslBypassConfiguration}.
*
* @return
* The {@link SslBypassConfiguration}.
*/
public SslBypassConfiguration getSslConfiguration()
{
return this.sslBypassConfiguration;
}
}
package com.company.project.mobile.beans.ssl;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import com.company.project.utils.StringUtils;
/**
* This {@link HostnameVerifier} will ignore the SSL validation for hostnames that are on the white-list.
*
* @author Matthew Weiler
*/
@Component
public class SslBypassConfiguration
{
/* PRIVATE VARIABLES */
@Value("${application.ssl.bypass.enabled}")
private String sslVerificationBypassEnabled;
@Autowired
private WhitelistedConfig whitelistedConfig;
/* PUBLIC METHODS */
/**
* This method will get the boolean flag to denote if the SSL handshake bypass is enabled.
*
* @return
* The boolean flag to denote if the SSL handshake bypass is enabled.
*/
public boolean isSslVerificationBypassEnabled()
{
return StringUtils.isTrue(this.sslVerificationBypassEnabled, false);
}
/**
* This method will get the {@link Map} of white-listed hostnames and their logical names as keys.
*
* @return
* The {@link Map} of white-listed hostnames and their logical names as keys.
*/
public Map<String, String> getWhitelistedHostnames()
{
return this.whitelistedConfig.getHostnames();
}
/* PUBLIC CLASSES */
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "application.ssl.bypass.whitelisted")
public class WhitelistedConfig
{
/* PRIVATE VARIABLES */
private Map<String, String> hostnames = new HashMap<String, String>();
/* PUBLIC METHODS */
/**
* This method will get the {@link Map} of white-listed hostnames and their logical names as keys.
*
* @return
* The {@link Map} of white-listed hostnames and their logical names as keys.
*/
public Map<String, String> getHostnames()
{
return this.hostnames;
}
}
}
Upvotes: 0