Spring RestTemplate接收sun.security.validator.ValidatorException:PKIX路径构建失败:

时间:2018-02-07 19:01:02

标签: java spring https resttemplate

我正在尝试连接到通过HTTPS的第三方网址。他们告诉我们,我们所需要的只是向他们发送一个HTTPS POST(不需要证书/密钥库)。但是,我收到以下例外:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://mep.moxtra.com/sample/api/v1/auth/cus/token": 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:666)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407)
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:1959)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1514)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:359)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:89)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
... 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:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
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:1496)
... 56 more

这是我的代码,我设法实现了一些允许Spring RestTemplate对HTTPS API进行POST的方法。

RestTemplate restTemplate = new RestTemplate(buildCustomRequestFactory());
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//This will only add the parameters I need on the body to call the POST operation
MultiValueMap<String, String> parametersMap = buildParametersMap(hubUserInfo);

HttpEntity<MultiValueMap<String, String>> jsonRequestEntity = new HttpEntity<MultiValueMap<String,String>>(parametersMap, requestHeaders);
try {
    ResponseEntity<String> response = restTemplate.postForEntity(moxtraApiUrl, jsonRequestEntity, String.class);

    System.out.println(response.getBody());
} catch (Exception e) {
    e.printStackTrace();
}

创建CustomRequestFactory:

private HttpComponentsClientHttpRequestFactory buildCustomRequestFactory() {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    requestFactory.setHttpClient(httpClient);

    return requestFactory;
}

您是否知道我是否缺少某些配置或是否有另一种方法可以通过HTTPS实现POST请求?

提前致谢。

2 个答案:

答案 0 :(得分:2)

我设法通过向方法添加一些代码来解决SSLHandshakeException的问题:buildCustomRequestFactory

在此之前,我需要创建一个名为TrustAllStrategy的类,该类实现TrustStrategy

中的org.apache.http.ssl接口

就像这样简单:

public class TrustAllStrategy implements TrustStrategy {
/**
 * Implement strategy to always trust certificates.
 * @see {org.apache.http.ssl.TrustStrategy} TrustStrategy
 */
@Override
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
    return true;
}

}

然后我修改了方法buildCustomRequestFactory

public static HttpComponentsClientHttpRequestFactory buildCustomRequestFactory(String host, int port) throws MoxtraSingleSignOnProcessException {
        HttpComponentsClientHttpRequestFactory requestFactory = null;
        SSLConnectionSocketFactory sslSocketFactory = null;
        SSLContext sslContext = null;
        HttpClient httpClient = null;
        NoopHostnameVerifier hostNameVerifier = new NoopHostnameVerifier();
        HttpClientBuilder clientBuilder = HttpClientBuilder.create();

    try {
        sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustAllStrategy()).useProtocol(SecurityConstants.TLS_V_12).build();
        sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostNameVerifier);
        clientBuilder.setSSLSocketFactory(sslSocketFactory);

        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(SecurityConstants.HTTPS, sslSocketFactory)
                .register(SecurityConstants.HTTP, PlainConnectionSocketFactory.getSocketFactory()).build();

        PoolingHttpClientConnectionManager clientConnectionMgr = new PoolingHttpClientConnectionManager(registry);
        HttpHost customHttpHost = new HttpHost(host, port);
        clientConnectionMgr.setSocketConfig(customHttpHost, SocketConfig.custom().setSoTimeout(SOCKET_TIME_OUT).build());

        httpClient = HttpClients.custom().setConnectionManager(clientConnectionMgr).build();
        requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
    } catch (Exception e) {
        throw new MoxtraSingleSignOnProcessException("A problem occured when tried to setup SSL configuration for API call", e);
    }

    return requestFactory;
}

主机和端口值来自服务提供商。然后我创建了一个配置为使用TLSv1.2的上下文(因为这是我被提供者使用的那个)。

之后我添加了一些HTTPS和HTTP的注册表配置,并且只是将连接管理器对象配置为有一个Socket Time Out。

通过这个我能够打电话给服务。

Rest模板对象的创建也改为传递参数:

RestTemplate restTemplate = new RestTemplate(buildCustomRequestFactory(host, port));

答案 1 :(得分:0)

我解决了实现此baeldung教程的Resttemplate配置的问题。很简单。

https://www.baeldung.com/spring-boot-https-self-signed-certificate

@Value("${trust.store}")
private Resource trustStore;

@Value("${trust.store.password}")
private String trustStorePassword;

@Bean
RestTemplate restTemplate() throws Exception {
     SSLContext sslContext = new SSLContextBuilder()
          .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
          .build();
     SSLConnectionSocketFactory socketFactory = new 
             SSLConnectionSocketFactory(sslContext);
     HttpClient httpClient = HttpClients.custom()
                             .setSSLSocketFactory(socketFactory)
                             .build();
     HttpComponentsClientHttpRequestFactory factory = 
                              new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);

}

在application.properties中添加:

#trust store location
trust.store=classpath:certificate-example.p12
#trust store password
trust.store.password=yourpassword

希望对您有所帮助。