I am getting an exception with the SslBundles not being able to load the keystore. This worked fine with Spring Boot 3.3.6 (and previous levels). The file key.jks is in my current directory, from where the app is being run via gradle bootRun.

Configuration:

spring:
  jms:
    cache:
      enabled: true
  ssl:
    bundle:
      jks:
        ibmmq:
          truststore:
            location: "key.jks"
            password: "passw0rd"
            type: "JKS"
          keystore:
            location: "key.jks"
            password: "passw0rd"
            type: "JKS"

Exception stack:

Failed to instantiate [org.springframework.jms.connection.CachingConnectionFactory]: Factory method 'cachingJmsConnectionFactory' threw exception with message: Could not load SSL context: Unable to create key store: Could not load store from 'key.jks'
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:645)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1351)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1181)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1122)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1093)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1030)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)

with the root of the exception stack being

Caused by: java.lang.IllegalStateException: Could not load store from 'key.jks'
        at org.springframework.boot.ssl.jks.JksSslStoreBundle.loadKeyStore(JksSslStoreBundle.java:140)
        at org.springframework.boot.ssl.jks.JksSslStoreBundle.createKeyStore(JksSslStoreBundle.java:107)
        ... 39 common frames omitted
Caused by: java.io.FileNotFoundException: class path resource [key.jks] cannot be opened because it does not exist
        at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:215)
        at org.springframework.boot.ssl.jks.JksSslStoreBundle.loadKeyStore(JksSslStoreBundle.java:135)
        ... 40 common frames omitted

Comment From: wilkinsona

Thanks for the report. It may be due to https://github.com/spring-projects/spring-boot/issues/42835, but I cannot be certain as the problem does not reproduce if I try to recreate what you've described above. Most likely because nothing's using the SSL bundle that you've configured.

By specifying only key.jks as the location, it's ambiguous how that resource should be loaded. An attempt is being made to load it using the classpath and this failed. That makes sense as, judging by what you have described, key.jks will not be on the classpath. You could try file:key.jks instead to make it clear that you want to load the store from the file system.

If the above does not help and/or you'd like us to spend some time investigating further, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: ibmmqmet

Adding file: does get it to work. I don't remember seeing that syntax in the original docs, and I based it on finding other examples of using raw filenames for keystores in Spring Boot projects. But perhaps I missed this being different.

Thanks for the quick response.

Comment From: wilkinsona

Thanks for confirming that it works with the file: prefix. I'm going to re-open this as I'd like us to look at whether we can restore the old behavior in this area and also keep the benefits of the recent changes around resource loading.

Comment From: NYPD

I ran into a similar issue, but with a different error message:

***************************
APPLICATION FAILED TO START
***************************

Description:

The content of 'keystore.location' from bundle 'myBundle' is not watchable'. Only 'file:' resources are watchable, but '/usr/temp/certs/my-dev-keystore-pkcs12.p12' has been set

Action:

Update your application to correct the invalid configuration:
Either use a watchable resource, or disable bundle reloading by setting reload-on-update = false on the bundle.
spring.ssl.bundle.jks:
  myBundle:
    reload-on-updates: true
    keystore:
      location: /usr/temp/certs/my-dev-keystore-pkcs12.p12
      password: superSecretPassword
      type: "PKCS12"

Adding file: in front of my location worked as well.

Comment From: darchangels13

This introduced a breaking change for me. While adding the file: prefix does allow the spring context to stand up, an unrelated 3rd party library is reading the same SSL properties and unfortunately doesn't handle the prefix properly.

At this time I'm considering whether I have to override the third party with a nearly-but-not-quite-duplicative value or figure out how to restore the original behavior.

Comment From: 72wildcard

Hi,

Quick question, currently migrating from spring-boot 3.2.6 to 3.4.1. Still experiencing the same behaviour, although this ticket was closed mentioning 3.4.1. Same thing, bundles do not load, file in the resource folder not found, fixing with file: works.

Any thoughts?

Comment From: philwebb

@72wildcard, that's annoying because we thought this fix would cover all situations. Perhaps we missed one. Could you please open a new issue and provide a sample that replicates the problem?

Comment From: lauhin

Still having this problem using 3.4.1 reactive. Prefixing the spring.ssl.bundle.jks.asa-server-ssl-reactive.keystore.location with file: makes the service starting up. But the SSL health indicator (https://docs.spring.io/spring-boot/api/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.html) still marks the app as unhealthy due to the same error...

Disabling the SSL health indicator with management.health.ssl.enabled=false marks the service green again in spring boot admin.

Comment From: wilkinsona

Thanks, @lauhin. Could you please open a new issue so that we can investigate?