Using a custom truststore and keystore in Spring Webclient

1k views Asked by At

I am trying to set a custom truststore and a custom keystore in Spring Webclient. This is needed to communicate with a backend server that uses a self signed certificate for https. I followed several guides to do this but was unable to get a connection. We are using p12 files as keystore and truststore and this worked fine in the old implementation with Spring WebServiceTemplate. My bean configuration looks like this:

@Bean
public SsiClient ssiClient(
    final ClientHttpConnector clientHttpConnector,
    final String url,
    final String urlAlternative
)
{
    final WebClient.Builder wcBuilder = WebClient.builder();

    return new SsiClient(wcBuilder
                             .clientConnector(clientHttpConnector)
                             .baseUrl(url)
                             .defaultHeaders(httpHeaders -> {
                                 httpHeaders.add(HttpHeaders.CONTENT_ENCODING, StandardCharsets.ISO_8859_1.toString());
                                 httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE);
                             })
                             .codecs(clientCodecConfigurer -> {
                                 clientCodecConfigurer.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
                                 clientCodecConfigurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder());
                             })
                             .build(), url, urlAlternative);
}

@Bean
public ClientHttpConnector clientHttpConnector(
    final SslContext sslContext
)
{
    return new ReactorClientHttpConnector(
        HttpClient.create()
                  .secure(
                      sslSpec -> {
                          sslSpec.sslContext(sslContext);
                      }
                  )
    );
}

@Bean
public SslContext sslContext(
    final String keyStorePath,
    final String keyStorePassword,
    final String keyStoreType,
    final String trustStorePath,
    final String trustStorePassword,
    final String trustStoreType
)
{
    try (
        final FileInputStream keyStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(keyStorePath));
        final FileInputStream trustStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(trustStorePath))
    )
    {
        final KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(keyStoreFileInputStream, keyStorePassword.toCharArray());
        final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());

        final KeyStore trustStore = KeyStore.getInstance("PKCS12");
        trustStore.load(trustStoreFileInputStream, trustStorePassword.toCharArray());
        final TrustManagerFactory trustManagerFactory = InsecureTrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

        return SslContextBuilder.forClient()
                                .keyManager(keyManagerFactory)
                                .trustManager(trustManagerFactory)
                                .build();

    }
    catch (final Exception e)
    {
        log.error("An error has occurred: ", e);
    }
    return null;
}

But this ends in an error with the following stacktrace:

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 java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
    at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306)
    at java.base/sun.security.validator.Validator.validate(Validator.java:264)
    at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:313)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:276)
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:141)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:623)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:464)
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:360)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)
    at io.netty.handler.ssl.SslHandler.runAllDelegatedTasks(SslHandler.java:1550)
    at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1564)
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1448)
    at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1275)
    at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1322)
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
    at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
    ... 38 common frames omitted

I am almost certain that this is a configuration error but I cannot find the cause.

0

There are 0 answers