我正在开发一个 SpringBoot 应用程序,该应用程序需要对使用自签名证书的内部 API 进行 RESTful 调用。
我只在 DEV 和 QA 中遇到这个问题,因为在 UAT 和 PROD 中他们使用的是正确签名的证书。
我正在 Windows 10 机器上开发并使用 Java 8。
我试过以下没有运气:
向 Windows 受信任证书添加了证书
- 我从 Chrome 下载了证书(在显示证书无效的地址栏中)。
- 然后,在 Windows 资源管理器中,我右键单击证书文件并选择安装证书并按照向导进行操作。
添加代码以忽略 SSL 验证
我在创建 RestTemplate 时调用了SSLUtils.buildRestTemplate方法。
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);
}
}
可能的想法
是否有可以接受 HTTP 连接并将它们路由到外部托管的 HTTPS 端点的应用程序?
此应用程序必须忽略任何证书问题。