Soap 客户端,TLS 1.2 连接 - PKIX 路径构建失败 - 尽管 cacerts 包含证书

Soap client, TLS 1.2 connection - PKIX path building failed - despite of cacerts contains certificates

我正在尝试在第一个连接阶段使用 TLS 连接作为 Soap 客户端连接到 3d 方 Web 服务。技术堆栈:Spring boot 2.2.2.RELEASEspring-ws-security 2.2.2.RELEASEhttpclient 4.5.4wss4j 1.6.19... 详见下文 pom.xml

这是我的 Spring 引导配置文件

@Configuration
public class SoapConfig {

    @Value("${client.ws.url}")
    private String defaultUri;

    @Value("${client.ssl.trust-store}")
    private Resource sslTrustStore;

    @Value("${client.ssl.trust-store-password}")
    private String sslTrustStorePassword;

    @Value("${client.wss.trust-store}")
    private Resource wssTrustStore;

    @Value("${client.wss.trust-store-password}")
    private String wssStorePassword;

    @Bean
    public Jaxb2Marshaller jaxb2Marshaller(){
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setPackagesToScan("com.advantio.models.soap.bindings");
        return jaxb2Marshaller;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate() throws Exception {
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setMarshaller(jaxb2Marshaller());
        webServiceTemplate.setUnmarshaller(jaxb2Marshaller());
        webServiceTemplate.setDefaultUri(defaultUri);
        webServiceTemplate.setMessageSender(httpComponentsMessageSender());

        //ClientInterceptor[] interceptors = new ClientInterceptor[]{securityInterceptor()};
        //webServiceTemplate.setInterceptors(interceptors);

        return webServiceTemplate;
    }

    @Bean
    public HttpComponentsMessageSender httpComponentsMessageSender() throws Exception {
        HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
        httpComponentsMessageSender.setHttpClient(httpClient());

        return httpComponentsMessageSender;
    }

    public HttpClient httpClient() throws Exception {
        return HttpClientBuilder.create()
                .setSSLSocketFactory(sslConnectionSocketFactory())
                .addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor()).build();
    }

    public SSLConnectionSocketFactory sslConnectionSocketFactory() throws Exception {
        // NoopHostnameVerifier essentially turns hostname verification off as otherwise following error
        // is thrown: java.security.cert.CertificateException: No name matching localhost found
        return new SSLConnectionSocketFactory(sslContext(), NoopHostnameVerifier.INSTANCE);
    }

    public SSLContext sslContext() throws Exception {
        return SSLContextBuilder.create()
                .loadTrustMaterial(wssTrustStore.getFile(), sslTrustStorePassword.toCharArray()).build();
    }

}

我的SoapClient.java

@Service
public class SoapClient {


    @Autowired
    private WebServiceTemplate webServiceTemplate;

    public ProvisionResponse getItemInfo(ProvisionData itemRequest){

        return (ProvisionResponse) webServiceTemplate.marshalSendAndReceive(itemRequest);
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.myproject</groupId>
    <artifactId>myproject</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.ws.security</groupId>
            <artifactId>wss4j</artifactId>
            <version>1.6.19</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-security</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.4</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
            <version>1.4.194</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.jks</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.jks</include>
                </includes>
            </resource>
        </resources>

    </build>


</project>

我已将所有证书添加到 cacerts 文件(对于:jdk 和 jre 文件夹) 这是一个屏幕 - https://www.screencast.com/t/E5NpGSCR

我得到以下堆栈跟踪错误:

chain [0] = ...
chain [1] = [
[... 5E   6B AC EE C4 80 9A 12 72  WT.....^k......r
00A0: FA 56 93 D7 FF BF 30 85   06 30 BF 0B 7F 4E FF 57  .V....0..0...N.W
00B0: 05 9D 24 ED 85 C3 2B FB   A6 75 A8 AC 2D 16 EF 7D  ..$...+..u..-...
00C0: 79 27 B2 EB C2 9D 0B 07   EA AA 85 D3 01 A3 20 28  y'............ (
00D0: 41 59 43 28 D2 81 E3 AA   F6 EC 7B 3B 77 B6 40 62  AYC(.......;w.@b
00E0: 80 05 41 45 01 EF 17 06   3E DE C0 33 9B 67 D3 61  ..AE....>..3.g.a
00F0: 2E 72 87 E4 69 FC 12 00   57 40 1E 70 F5 1E C9 B4  .r..i...W@.p....

]
***
%% Invalidated:  [Session-2, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]
my-scheduled-task-pool-3, SEND TLSv1.2 ALERT:  fatal, description = certificate_unknown
my-scheduled-task-pool-3, WRITE: TLSv1.2 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 03 00 02 02 2E                               .......
my-scheduled-task-pool-3, called closeSocket()
my-scheduled-task-pool-3, handling exception: 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
2020-01-24T09:03:11,832 ERROR [my-scheduled-task-pool-3] o.s.s.s.TaskUtils$LoggingErrorHandler: Unexpected error occurred in scheduled task
org.springframework.ws.client.WebServiceIOException: I/O error: 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.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:561)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:373)
    at com.myproject.service.impl.SoapClient.getItemInfo(SoapClient.java:20)
    at com.myproject.service.impl.TestSenderImpl.send(TestSenderImpl.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
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:1946)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
    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:373)
    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:108)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at org.springframework.ws.transport.http.HttpComponentsConnection.onSendAfterWrite(HttpComponentsConnection.java:121)
    at org.springframework.ws.transport.AbstractWebServiceConnection.send(AbstractWebServiceConnection.java:48)
    at org.springframework.ws.client.core.WebServiceTemplate.sendRequest(WebServiceTemplate.java:658)
    at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:606)
    at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
    ... 19 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:262)
    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:1621)
    ... 44 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:141)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
    ... 50 more
2020-01-24T09:03:17,995 DEBUG [my-scheduled-task-pool-5] c.a.s.i.Stage1RepeaterImpl: STAGE_1 Starts work...
Hibernate: select transactio0_.trans_id as trans_id1_1_, transactio0_.number_of_attempts as number_o2_1_, transactio0_.comment as comment3_1_, transactio0_.created as created4_1_, transactio0_.error_code as error_co5_1_, transactio0_.external_id as external6_1_, transactio0_.file_path as file_pat7_1_, transactio0_.first_name as first_na8_1_, transactio0_.last_name as last_nam9_1_, transactio0_.last_updated as last_up10_1_, transactio0_.otp as otp11_1_, transactio0_.otp_enc as otp_enc12_1_, transactio0_.phone as phone13_1_, transactio0_.phone_enc as phone_e14_1_, transactio0_.DATA_block as DATA_blo15_1_, transactio0_.status as status16_1_ from transactions transactio0_ where transactio0_.status=? order by transactio0_.created desc limit ?
2020-01-24T09:03:17,998 DEBUG [my-scheduled-task-pool-5] c.a.s.i.Stage1RepeaterImpl: my-scheduled-task-pool-5 STAGE_1 Finished work...
2020-01-24T09:03:17,999 DEBUG [my-scheduled-task-pool-5] c.a.s.i.DATAServiceImpl: SEND DATA Started work...
Hibernate: select transactio0_.trans_id as trans_id1_1_, transactio0_.number_of_attempts as number_o2_1_, transactio0_.comment as comment3_1_, transactio0_.created as created4_1_, transactio0_.error_code as error_co5_1_, transactio0_.external_id as external6_1_, transactio0_.file_path as file_pat7_1_, transactio0_.first_name as first_na8_1_, transactio0_.last_name as last_nam9_1_, transactio0_.last_updated as last_up10_1_, transactio0_.otp as otp11_1_, transactio0_.otp_enc as otp_enc12_1_, transactio0_.phone as phone13_1_, transactio0_.phone_enc as phone_e14_1_, transactio0_.DATA_block as DATA_blo15_1_, transactio0_.status as status16_1_ from transactions transactio0_ where transactio0_.status=? order by transactio0_.created desc limit ?
2020-01-24T09:03:18,002 DEBUG [my-scheduled-task-pool-4] c.a.s.i.Stage2RepeaterImpl: STAGE_2 Starts work...
2020-01-24T09:03:18,003 DEBUG [my-scheduled-task-pool-5] c.a.s.i.DATAServiceImpl: my-scheduled-task-pool-5 SEND DATA Finished work...
Hibernate: select transactio0_.trans_id as trans_id1_1_, transactio0_.number_of_attempts as number_o2_1_, transactio0_.comment as comment3_1_, transactio0_.created as created4_1_, transactio0_.error_code as error_co5_1_, transactio0_.external_id as external6_1_, transactio0_.file_path as file_pat7_1_, transactio0_.first_name as first_na8_1_, transactio0_.last_name as last_nam9_1_, transactio0_.last_updated as last_up10_1_, transactio0_.otp as otp11_1_, transactio0_.otp_enc as otp_enc12_1_, transactio0_.phone as phone13_1_, transactio0_.phone_enc as phone_e14_1_, transactio0_.DATA_block as DATA_blo15_1_, transactio0_.status as status16_1_ from transactions transactio0_ where transactio0_.status=? order by transactio0_.created desc limit ?
2020-01-24T09:03:18,006 DEBUG [my-scheduled-task-pool-4] c.a.s.i.Stage2RepeaterImpl: my-scheduled-task-pool-4 STAGE_2 Finished work...
Disconnected from the target VM, address: '127.0.0.1:58568', transport: 'socket'

Process finished with exit code -1

不明白为什么 PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Jdk 版本 1.8.0_191,但也尝试了 jdk 1.8.0_241jdk 13 Maven 版本 3.6.0

好的,我找到问题了。 在创建 sslContext 时,我不得不使用 .loadKeyMaterial 而不是 .loadTrustStore 方法。这里改成sslContext()方法:

public SSLContext sslContext() throws Exception {


        KeyStore keyStore = KeyStore.getInstance("JKS");
        InputStream in = new FileInputStream(sslTrustStore.getFile());
        keyStore.load(in, sslTrustStorePassword.toCharArray());

        return SSLContextBuilder.create()
                .loadKeyMaterial(keyStore, sslTrustStorePassword.toCharArray(), (a, s)->"alias-name").build();

    }