0

私は、自己署名証明書を使用する内部 API への RESTful 呼び出しを行う必要がある SpringBoot アプリケーションに取り組んでいます。

UAT と PROD では適切に署名された証明書を使用しているため、この問題は DEV と QA でのみ発生しています。

Windows 10 マシンで Java 8 を使用して開発しています。


私は運がないので以下を試しました:

Windows の信頼できる証明書に証明書を追加

  1. Chrome から証明書をダウンロードしました (証明書が無効であることを示すアドレス バー)。
  2. 次に、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 エンドポイントにルーティングできるアプリケーションはありますか?

このアプリケーションは、証明書の問題を無視する必要があります。

4

3 に答える 3