简体   繁体   English

无法从Spring应用程序启动多个HTTPS连接

[英]Impossible to start multiple HTTPS connections from Spring application

I have a Spring Boot application that tries to open a javax.net.ssl.HttpsURLConnection to a server but the response received is: java.io.IOException: Server returned HTTP response code: 403 for URL: https://serverIP:8443/path 我有一个Spring Boot应用程序试图打开一个javax.net.ssl.HttpsURLConnection到服务器,但收到的响应是: java.io.IOException: Server returned HTTP response code: 403 for URL: https://serverIP:8443/path

When the keyStore , trustStore and their passwords are set as system properties the request works correctly and the expected JSON response is received: keyStoretrustStore及其密码设置为系统属性时 ,请求正常工作并收到预期的JSON响应:

System.setProperty("javax.net.ssl.keyStore", "src/main/resources/myKeyStore.p12");
System.setProperty("javax.net.ssl.trustStore", "src/main/resources/myTrustStore.truststore");
System.setProperty("javax.net.ssl.keyStorePassword", "myPassword");
System.setProperty("javax.net.ssl.trustStorePassword", "myPassword");

But the 403 response code is received when trying to set the information in SSLContext , instead of setting the system properties, by using this method that returns an SSLContext object: 但是,尝试通过使用返回SSLContext对象的此方法设置SSLContext中的信息而不是设置系统属性时,会收到403响应代码:

public static SSLContext getSslContext(String trustStoreFile, String keystoreFile, String password)
            throws GeneralSecurityException, IOException {
        final KeyStore keystore = KeyStore.getInstance("pkcs12"); // also tried with JKS

        try (final InputStream inKeystore = new FileInputStream(keystoreFile)) {
            keystore.load(inKeystore, password.toCharArray());
        }

        try (final InputStream inTruststore = new FileInputStream(trustStoreFile)) {
            keystore.load(inTruststore, password.toCharArray());
        }

        final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); // also tried with .getDefaultAlgorithm()
        keyManagerFactory.init(keystore, password.toCharArray());

        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keystore);

        X509TrustManager x509Tm = null;
        for (final TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
            if (trustManager instanceof X509TrustManager) {
                x509Tm = (X509TrustManager) trustManager;
                break;
            }
        }

        final X509TrustManager finalTm = x509Tm;
        final X509ExtendedTrustManager customTm = new X509ExtendedTrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return finalTm.getAcceptedIssuers();
            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
            }
        };

        final SSLContext sslContext = SSLContext.getInstance("TLS"); // also tried with SSL
        sslContext.init(
                keyManagerFactory.getKeyManagers(),
                new TrustManager[]{customTm},
                new SecureRandom());

        final HostnameVerifier allHostsValid = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

        return sslContext;
    }

OBS : The trustStore and keyStore have the same password, that's why the method has only one password parameter and used for both key and trust manager factories. OBS :trustStore和keyStore具有相同的密码,这就是该方法只有一个密码参数并用于密钥管理器和信任管理器工厂的原因。

The way the getSslContext method is called and used is: 调用和使用getSslContext方法的方式是:

        final SSLContext sslContext = SSLContextHelper.getSslContext("src/main/resources/myTrustStore.truststore",
                                                                     "src/main/resources/myKeyStore.p12", 
                                                                     "myPassword");
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        final URL url = new URL("https://serverIP:8443/path");
        final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setSSLSocketFactory(sslSocketFactory);

        // tried adding some headers to the request
        urlConnection.addRequestProperty("Content-Type", "application/json");
        urlConnection.addRequestProperty("Accept", "application/json");
        urlConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
        urlConnection.connect();

        final InputStream inputstream = urlConnection.getInputStream();

The error is thrown at the last line when trying to get the inputStream of the URL connection. 尝试获取URL连接的inputStream时,在最后一行抛出错误。

Also, I tried using the following classes from org.apache.http : SSLConnectionSocketFactory, HttpClient, HttpGet, HttpResponse but response code is still 403. 另外,我尝试使用org.apache.http的以下类: SSLConnectionSocketFactory, HttpClient, HttpGet, HttpResponse但响应代码仍为403。

I can only think that there is something missing from the SSL configuration because the system properties work. 我只能认为SSL配置中缺少某些东西,因为系统属性有效。 Any suggestions on what I miss setting in the SSLContext/SSLSocketFactory or how can I solve/better debug the problem are welcome! 任何有关我在SSLContext/SSLSocketFactory设置错误的建议,或者我如何解决/更好地调试问题都是受欢迎的! Thanks! 谢谢!

I managed to open the HTTPS connections only by using Spring's RestTemplate ( org.springframework.web.client.RestTemplate ) that uses the org.apache.http.client.HttpClient . 我设法只使用使用org.apache.http.client.HttpClient Spring的RestTemplateorg.springframework.web.client.RestTemplate )来打开HTTPS连接。

The method for getting the RestTemplate that has in its SSLContext the keyStore, trustStore and their passwords is the following: SSLContext获取RestTemplate的方法是keyStore,trustStore及其密码如下:

public RestTemplate getRestTemplate(final String keyStoreFile, final String trustStoreFile,
                                    final String password) throws Exception {

    final SSLContext sslContext = SSLContextBuilder.create()
                                                   .loadKeyMaterial(ResourceUtils.getFile(keyStoreFile), password.toCharArray(), password.toCharArray())
                                                   .loadTrustMaterial(ResourceUtils.getFile(trustStoreFile), password.toCharArray())
                                                   .build();

    final HttpClient client = HttpClients.custom()
                                         .setSSLContext(sslContext)
                                         .build();

    final HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
    httpComponentsClientHttpRequestFactory.setHttpClient(client);

    return new RestTemplate(httpComponentsClientHttpRequestFactory);
}

The way that the RestTemplate is used for the HTTPS call is: RestTemplate用于HTTPS调用的方式是:

final String keyStoreFile = "src/main/resources/myKeyStore.p12";
final String trustStoreFile = "src/main/resources/myTrustStore.truststore";
final String password = "myPassword"; // same password for keyStore and trustStore
final String response = getRestTemplate(keyStoreFile, trustStoreFile, password).getForObject("https://serverIP:8443/path", String.class);
LOGGER.info("Response received: " + response);

Hope this helps anyone, had a lot of struggle with the HTTPS connections :) 希望这有助于任何人,与HTTPS连接有很多斗争:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM