Spring Rest 模板:主机名“localhost"与对等方提供的证书主题不匹配 [英] Spring Rest Template : Host name 'localhost' does not match the certificate subject provided by the peer

查看:58
本文介绍了Spring Rest 模板:主机名“localhost"与对等方提供的证书主题不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我像这样使用 RestTemplate 配置:

I use RestTemplate config like this :

private RestTemplate createRestTemplate() throws Exception {
        final String username = "admin";
        final String password = "admin";
        final String proxyUrl = "localhost";
        final int port = 443;

        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(proxyUrl, port),
                new UsernamePasswordCredentials(username, password));

        HttpHost host = new HttpHost(proxyUrl, port, "https");

        HttpClientBuilder clientBuilder = HttpClientBuilder.create();

        clientBuilder.setProxy(host).setDefaultCredentialsProvider(credsProvider).disableCookieManagement();

        HttpClient httpClient = clientBuilder.build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setHttpClient(httpClient);

        return new RestTemplate(factory);
    }

这就是我的方法的工作原理:

And the this is how my method work:

public String receiveMessage(String message) {
        try {
            restTemplate = createRestTemplate();
            ObjectMapper mapper = new ObjectMapper();
            Class1 class1 = null;
            String json2 = "";

            class1= mapper.readValue(message, Class1.class);

            Class1 class2 = restTemplate.getForObject(URL_SERVICE_1 + "/class1/findByName?name=" + class1.getName(),
                    Class1.class);
            System.out.println("Server 1 : " + message);
            json2 = mapper.writeValueAsString(class2);

            return "Error - " + json2;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            return e.getMessage();
        }

    }

URL_SERVICE_1 包含 https://localhost

URL_SERVICE_1 contains https://localhost

当我尝试调用函数 GET 时,我总是得到这样的返回:

When I tried to call function GET, I always get return like this :

I/O error on GET request for "https://localhost/class1/findByName?name=20-1P": Host name 'localhost' does not match the certificate subject provided by the peer (CN=*.webku-cool.com, OU=EssentialSSL Wildcard, OU=Domain Control Validated); nested exception is javax.net.ssl.SSLPeerUnverifiedException: Host name 'localhost' does not match the certificate subject provided by the peer (CN=*.webku-cool.com, OU=EssentialSSL Wildcard, OU=Domain Control Validated)

我不知道带有 https 的 restTemplate 的正确设置.我已经尝试了 23 个关于 SSL 设置的参考,但得到了同样的错误.

I don't know the correct setting for restTemplate with https. I already tried 23 references about SSL Settings and got same error.

1.参考

2.参考

3.参考

推荐答案

此问题的正确解决方案是通过将 localhost 添加到主题列表来更正 ssl 证书.但是,如果您的意图是为了开发目的而绕过 ssl,则需要定义一个连接工厂,该工厂始终将主机名验证的结果返回为 true.

The correct solution for this problem is to correct the ssl certificate by adding localhost to the list of subjects. However, if your intent is to bypass ssl for development purpose, you would need to define a connection factory which always returns the result of hostname verification as true.

SSLClientHttpRequestFactory

public class SSLClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

    @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() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }

                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() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            super.prepareConnection(httpsConnection, httpMethod);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
     * see
     * http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566
     * -2342133.html (Java 8 section)
     */
    private static class MyCustomSSLSocketFactory extends SSLSocketFactory {

        private final SSLSocketFactory delegate;

        public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
            this.delegate = delegate;
        }

        @Override
        public String[] getDefaultCipherSuites() {
            return delegate.getDefaultCipherSuites();
        }

        @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 {
            final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final String host, final int port) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final String host, final int port, final InetAddress localAddress,
                final int localPort) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final InetAddress host, final int port) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
                final int localPort) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
            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;
        }
    }
}

并使用上面提到的连接工厂作为 RestTemplate 的构造函数参数.覆盖主机名验证始终返回true的部分代码如下:

And use the above mentioned connection factory as the constructor argument for RestTemplate. The part of the code which overrides the host name verification to always return true is as follows:

httpsConnection.setHostnameVerifier(new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

快乐编码!

这篇关于Spring Rest 模板:主机名“localhost"与对等方提供的证书主题不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆