如何在Spring RestTemplate中禁用SSL证书检查?

78

我正在尝试编写一项与我们的测试相关的集成测试,其中我们的测试使用Simple启动了一个嵌入式HTTPS服务器。我使用keytool创建了自签名证书,并且能够通过浏览器(具体来说是Chrome,我确实收到了关于自签名证书的警告)访问该服务器。

然而,当我尝试使用Spring RestTemplate连接时,我会遇到ResourceAccessException错误:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8088":sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:444)
    at net.initech.DummySslServer.shouldConnect(DummySslServer.java:119)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1917)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:301)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:295)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1369)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:156)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:925)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:860)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1043)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1355)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:52)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:541)
    ... 33 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1351)
    ... 47 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 53 more

我看到了一些关于替换HostnameVerifier的建议,这些建议来自其他问题博客 文章

private static final HostnameVerifier PROMISCUOUS_VERIFIER = ( s, sslSession ) -> true;

我已经将其设置为全局和RestTemplate本身:

HttpsURLConnection.setDefaultHostnameVerifier( PROMISCUOUS_VERIFIER );

...并且在RestTemplate本身上:

final RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory( new SimpleClientHttpRequestFactory() {
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if(connection instanceof HttpsURLConnection ){
            ((HttpsURLConnection) connection).setHostnameVerifier(PROMISCUOUS_VERIFIER);
        }
        super.prepareConnection(connection, httpMethod);
    }
});

然而,我仍然遇到上述错误。我该怎么解决?
  1. 在单元测试之外本地安装证书不是一个选项,因为这样需要在每台开发机器和构建服务器上手动安装它,并会引起一系列繁琐的程序。
  2. 我们需要SSL,因为我们正在测试一个基于RestTemplate的库,并且我们正在正确配置它。

我正在使用Java 8(但也可以使用7)和Spring 4.0.3。


您需要手动配置模板使用的HTTP连接器。 - chrylis -cautiouslyoptimistic-
@chrylis,我在Java的SDK或Spring的JavaDoc中都没有看到名为HttpConnector的类。你具体指的是哪个类? - Sled
你有没有在这里检查我的答案。这对我有效。 - nilesh
请参见https://dev59.com/6nRA5IYBdhLWcg3wsgLq。 - Raedwald
Sled,你能否确认一下你是如何解决这个问题的? - PAA
13个回答

0
在Apache HttpClient 5中,应该在ConnectionManager上设置SSLSocketFactory。这是@Debasis-Mishra答案的更新版本。
@Bean
@Qualifier("trust-all-certs")
public RestTemplate getTrustingRestTemplate() {
    try {
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
        HostnameVerifier hostnameVerifier = (s, sslSession) -> true;
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(csf).build();
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);

        return new RestTemplate(requestFactory);
    } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
        throw new RuntimeException(e);
    }
}

-1
在我的情况下,使用letsencrypt https时,这是由于在请求的服务器上将cert.pem作为证书文件而不是fullchain.pem。有关详细信息,请参见this thread

-31

禁用证书检查是错误的解决方案,而且极其不安全。

正确的解决方案是将自签名证书导入您的信任存储库。更正确的解决方案是通过CA签署证书。

如果这只是“仅用于测试”,仍然需要测试生产配置。测试其他内容根本不是测试,只是浪费时间。


5
这只是在一个单元测试中。在这个单元测试中,我会启动我们客户端服务器的虚拟版本,向它发送命令。在生产环境中,客户端服务器具有CA签名的证书。但是,他们没有测试环境。我们的许多命令都具有破坏性,因此我使用我们的虚拟客户端服务器记录我们发送给它的消息,以便我们可以检查它们是否符合要求。我们使用的库强制使用SSL,以捕获我们发送的消息的全部内容。 - Sled
10
不回答问题。有时为了调试目的,需要禁用SSL检查。 - Alex
10
@EJP这个问题明显在询问“如何使用Spring RestTemplate禁用SSL证书检查?”。你提供的答案并没有回答这个问题。因为我也在寻找答案,所以我在你的评论中没有找到它。 - Alex
4
在特定情况下禁用证书检查是有用的。应该根据具体情况进行审查,但你的回答中没有提供这样的功能。 - Gerard Downes
6
我正在开发一个内部网络服务,其中在私有网络中发送请求非常重要。要使用的资源位于https之后(不带可信CA,因为这显然是内部网络),无法通过http访问。未来,请避免在没有回答问题的情况下发布过于个人化的意见。评论区存在于此目的。 - Gyuhyeon Lee
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接