SSL issues after upgrading to Spring Boot 3 and JDK21 when using RestTemplate

193 views Asked by At

After upgrading from JDK 11 to 21 and Spring Boot 2 to Spring Boot 3.2.2 the service cannot contact another service using Spring's RestTemplate and fails with

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://our-network/api/of-the-other-service": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:905) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:885) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:781) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:529) ~[spring-web-6.1.3.jar:6.1.3]
    at com.company.WebhookService.notifyFailsafe(WebhookService.java:97) ~[main/:na]
    at com.company.WebhookService.lambda$notifySync$0(WebhookService.java:46) ~[main/:na]
    at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
    at com.company.WebhookService.notifySync(WebhookService.java:46) ~[main/:na]
    at com.company.WebhookService.notify(WebhookService.java:38) ~[main/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:351) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.1.3.jar:6.1.3]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385) ~[spring-tx-6.1.3.jar:6.1.3]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.1.3.jar:6.1.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.3.jar:6.1.3]
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:113) ~[spring-aop-6.1.3.jar:6.1.3]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:378) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:316) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:647) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:467) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:363) ~[na:na]
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:393) ~[na:na]
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:476) ~[na:na]
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:447) ~[na:na]
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201) ~[na:na]
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426) ~[na:na]
    at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:586) ~[na:na]
    at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:187) ~[na:na]
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:141) ~[na:na]
    at org.springframework.http.client.SimpleClientHttpRequest.executeInternal(SimpleClientHttpRequest.java:79) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:101) ~[spring-web-6.1.3.jar:6.1.3]
    at com.company.commons.HeaderRequestInterceptor.intercept(HeaderRequestInterceptor.java:33) ~[commons-1.2.6.jar:na]
    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:87) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:71) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-6.1.3.jar:6.1.3]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:879) ~[spring-web-6.1.3.jar:6.1.3]
    ... 23 common frames omitted
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:388) ~[na:na]
    at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:271) ~[na:na]
    at java.base/sun.security.validator.Validator.validate(Validator.java:256) ~[na:na]
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:230) ~[na:na]
    at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132) ~[na:na]
    at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:631) ~[na:na]
    ... 47 common frames omitted
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:148) ~[na:na]
    at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129) ~[na:na]
    at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297) ~[na:na]
    at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:383) ~[na:na]
    ... 52 common frames omitted

The certificate is installed on the machines I'm testing it and it perfectly worked with JDK 11.

My impression was, that Java uses the system's certificates by default and I haven't read anything opposing that for later JDKs.

However, I attempted to generate a custom .pkcs12 truststore (tried it also with .jks) with our certificate, using this script

#!/usr/bin/env bash

# Remote server details
REMOTE_HOST="our-cert-server"
REMOTE_PORT="636"

# Output filenames
SERVER_CERT_PEM="server_cert.pem"
SERVER_CERT_DER="server_cert.der"
TRUSTSTORE_P12="truststore.p12"

# Check if the truststore file already exists and remove it if so
if [ -f "${TRUSTSTORE_P12}" ]; then
    rm "${TRUSTSTORE_P12}"
    echo "Previous truststore removed."
fi

# Step 1: Retrieve server certificate
openssl s_client -showcerts -connect "${REMOTE_HOST}:${REMOTE_PORT}" </dev/null > "${SERVER_CERT_PEM}"

# Step 2: Convert server certificate to DER format
openssl x509 -in "${SERVER_CERT_PEM}" -outform der -out "${SERVER_CERT_DER}"

# Step 3: Combine server certificate into PKCS12 truststore
openssl pkcs12 -export -out "${TRUSTSTORE_P12}" -in "${SERVER_CERT_DER}" -nokeys -name "${REMOTE_HOST}"

echo "Truststore generated: ${TRUSTSTORE_P12}"

dropped that into the resources directory and added

server.ssl.trust-store=classpath:truststore.p12
server.ssl.trust-store-password=some-password

And this however produces the following error to me

Caused by: java.io.FileNotFoundException: /root/.keystore (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[na:na]
at java.base/java.io.FileInputStream.open(FileInputStream.java:213) ~[na:na]
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152) ~[na:na]
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106) ~[na:na]
at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:84) ~[na:na]
at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:180) ~[na:na]
at org.apache.catalina.startup.CatalinaBaseConfigurationSource.getResource(CatalinaBaseConfigurationSource.java:118) ~[tomcat-embed-core-10.1.18.jar!/:na]
at org.apache.tomcat.util.net.SSLUtilBase.getStore(SSLUtilBase.java:210) ~[tomcat-embed-core-10.1.18.jar!/:na]
at org.apache.tomcat.util.net.SSLHostConfigCertificate.getCertificateKeystore(SSLHostConfigCertificate.java:237) ~[tomcat-embed-core-10.1.18.jar!/:na]
at org.apache.tomcat.util.net.SSLUtilBase.getKeyManagers(SSLUtilBase.java:308) ~[tomcat-embed-core-10.1.18.jar!/:na]
at org.apache.tomcat.util.net.SSLUtilBase.createSSLContext(SSLUtilBase.java:268) ~[tomcat-embed-core-10.1.18.jar!/:na]
at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:104) ~[tomcat-embed-core-10.1.18.jar!/:na]
... 30 common frames omitted

Questions

  1. Why is the system's certificate not used anymore with the newer JDK by default?
  2. Why is using a truststore causing a keystore exception?

thanks in advance for any help :)

0

There are 0 answers