I have to deploy a Java application which uses Spring Boot and Spring Security. This application acts as a resource server to issue JWT.
Here is an extract of the local configuration application.yaml
:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8090/auth/realms/myrealm
It is working fine when the authorization server (keycloak) is running on localhost. However, when keycloak is deployed on a DNS name with a self-signed certificate, the application fails to start with the following error:
2022-01-28 09:17:40.438 INFO 1 --- [ main] i.k.k.front.MyApplication : Starting MyApplication using Java 11.0.12 on myapp-c8c7c5554-czpsd with PID 1 (/myapp/sbapp.jar started by spring in /myapp)
2022-01-28 09:17:40.495 INFO 1 --- [ main] i.k.k.front.MyApplication : No active profile set, falling back to default profiles: default
2022-01-28 09:18:11.465 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8000 (http)
2022-01-28 09:18:11.664 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-01-28 09:18:11.670 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.53]
2022-01-28 09:18:12.968 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-01-28 09:18:12.972 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 31543 ms
2022-01-28 09:18:53.802 WARN 1 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bea
n with name 'jwtDecoderByIssuerUri' defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration$JwtDecoderConfiguration.class]: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.jwt.JwtDecoder]: Factory method 'jwtDecoderByIssuerUri' threw exception; nested exception is java.lang.IllegalArgumentEx
ception: Unable to resolve the Configuration with the provided Issuer of "https://myhost/auth/realms/myrealm"
2022-01-28 09:18:53.897 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2022-01-28 09:18:54.189 INFO 1 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-01-28 09:18:54.583 ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtDecoderByIssuerUri' defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration$Jw
tDecoderConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.jwt.JwtDecoder]: Factory method 'jwtDecoderBy
IssuerUri' threw exception; nested exception is java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "https://myhost/auth/realms/myrealm"
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.10.jar!/:5.3.10]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.10.jar!/:5.3.10]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.5.jar!/:2.5.5]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.5.jar!/:2.5.5]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.5.jar!/:2.5.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.5.jar!/:2.5.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-2.5.5.jar!/:2.5.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) ~[spring-boot-2.5.5.jar!/:2.5.5]
at io.myapp.MyApplication .main(MyApplication .java:10) ~[classes!/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[sbapp.jar:na]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[sbapp.jar:na]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[sbapp.jar:na]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[sbapp.jar:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.jwt.JwtDecoder]: Factory method 'jwtDecoderByIssuerUri' threw exception; nested exception is java.lang.IllegalArgumentException: U
nable to resolve the Configuration with the provided Issuer of "https://myhost/auth/realms/myrealm"
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.10.jar!/:5.3.10]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.10.jar!/:5.3.10]
... 27 common frames omitted
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "https://myhost/auth/realms/myrealm"
at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getConfiguration(JwtDecoderProviderConfigurationUtils.java:143) ~[spring-security-oauth2-jose-5.5.2.jar!/:5.5.2]
at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(JwtDecoderProviderConfigurationUtils.java:76) ~[spring-security-oauth2-jose-5.5.2.jar!/:5.5.2]
at org.springframework.security.oauth2.jwt.JwtDecoders.fromIssuerLocation(JwtDecoders.java:101) ~[spring-security-oauth2-jose-5.5.2.jar!/:5.5.2]
at org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$JwtDecoderConfiguration.jwtDecoderByIssuerUri(OAuth2ResourceServerJwtConfiguration.java:95) ~[spring-boot-autoconfigure-2.5.5.jar!/:2.
5.5]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.10.jar!/:5.3.10]
... 28 common frames omitted
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://myhost/auth/realms/myrealm/.well-known/openid-configuration": 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: 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:785) ~[spring-web-5.3.10.jar!/:5.3.10]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:670) ~[spring-web-5.3.10.jar!/:5.3.10]
at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getConfiguration(JwtDecoderProviderConfigurationUtils.java:132) ~[spring-security-oauth2-jose-5.5.2.jar!/:5.5.2]
... 36 common frames omitted
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:131) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:349) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:287) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1357) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1232) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1175) ~[na:na]
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) ~[na:na]
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182) ~[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:1426) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1336) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:450) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:421) ~[na:na]
at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:572) ~[na:na]
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:197) ~[na:na]
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:168) ~[na:na]
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76) ~[spring-web-5.3.10.jar!/:5.3.10]
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-5.3.10.jar!/:5.3.10]
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-5.3.10.jar!/:5.3.10]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776) ~[spring-web-5.3.10.jar!/:5.3.10]
... 38 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:439) ~[na:na]
at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306) ~[na:na]
at java.base/sun.security.validator.Validator.validate(Validator.java:264) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:313) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:222) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129) ~[na:na]
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1341) ~[na:na]
... 56 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:141) ~[na:na]
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[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:434) ~[na:na]
... 62 common frames omitted
Here are the things I tried so far (which didn’t work):
- use keycloak in plain HTTP
- provide keycloak TLS certificate public key to application through
spring.security.oauth2.resourceserver.jwt.public-key-location
- provide keycloak TLS certificate + public key to application through
spring.security.oauth2.resourceserver.jwt.public-key-location
- provide a JKS trustore containing keycloak TLS certificate to application through
spring.security.oauth2.resourceserver.jwt.public-key-location
- provide a JKS trustore containing keycloak TLS certificate to application through
javax.net.ssl.trustStore
(along withjavax.net.ssl.trustStorePassword
andjavax.net.ssl.trustStoreType=jks
) - create a nginx proxy redirecting to deployed keycloak in order to provide a plain HTTP
localhost
value tospring.security.oauth2.resourceserver.jwt.issuer-uri
I might add that I’m not a Java developer, therefore I don’t have any way to alter the app code. I can only act on app configuration through command line arguments or application.yaml
.
As a side note, the keycloak server is working fine when deployed. I can access in my browser the URLs that Spring is complaining about, as long as I accept « the risk » of self-signed certificates.:
- https://myhost/auth/realms/myrealm
- https://myhost/auth/realms/myrealm/.well-known/openid-configuration