Describe the bug
When an Azure Key Vault contains multiple certificates and any one of them is disabled, the JCA KeyStore (AzureKeyVault / DKS) fails to initialize — even when the disabled certificate is not the alias referenced by the application's Spring SSL bundle.
KeyVaultCertificates.refreshCertificates() eagerly iterates over every alias in the vault and fetches the key, certificate, and certificate chain for each one. There is no per-certificate error handling, so a 403 Forbidden returned for a single disabled certificate propagates up, aborts KeyVaultKeyStore.<init>, and prevents the SSL bundle from being created. The Spring Boot application then fails to start.
The net effect: one disabled certificate takes down the whole application, regardless of whether that certificate is actually used.
Exception or Stack Trace
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.server.autoconfigure.servlet.ServletWebServerFactoryCustomizer]: Factory method 'servletWebServerFactoryCustomizer' threw exception with message: Error creating bean with name 'sslBundleRegistry' defined in class path resource [org/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.class]: Failed to instantiate [org.springframework.boot.ssl.DefaultSslBundleRegistry]: Factory method 'sslBundleRegistry' threw exception with message: Failed to load Key Vault keystore for SSL bundle 'client-bundle'
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:183) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiateWithFactoryMethod(SimpleInstantiationStrategy.java:72) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:152) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-7.0.8.jar:7.0.8]
... 33 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sslBundleRegistry' defined in class path resource [org/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.class]: Failed to instantiate [org.springframework.boot.ssl.DefaultSslBundleRegistry]: Factory method 'sslBundleRegistry' threw exception with message: Failed to load Key Vault keystore for SSL bundle 'client-bundle'
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:645) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1360) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1192) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:565) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:229) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1769) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider.getIfAvailable(DefaultListableBeanFactory.java:2575) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.boot.web.server.autoconfigure.servlet.ServletWebServerConfiguration.servletWebServerFactoryCustomizer(ServletWebServerConfiguration.java:69) ~[spring-boot-web-server-4.0.7.jar:4.0.7]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:155) ~[spring-beans-7.0.8.jar:7.0.8]
... 36 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.ssl.DefaultSslBundleRegistry]: Factory method 'sslBundleRegistry' threw exception with message: Failed to load Key Vault keystore for SSL bundle 'client-bundle'
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:183) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiateWithFactoryMethod(SimpleInstantiationStrategy.java:72) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:152) ~[spring-beans-7.0.8.jar:7.0.8]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-7.0.8.jar:7.0.8]
... 52 common frames omitted
Caused by: java.lang.RuntimeException: Failed to load Key Vault keystore for SSL bundle 'client-bundle'
at com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.AzureKeyVaultSslBundleRegistrar.initilizeKeyVaultKeyStore(AzureKeyVaultSslBundleRegistrar.java:158) ~[spring-cloud-azure-autoconfigure-7.3.0.jar:7.3.0]
at com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.AzureKeyVaultSslBundleRegistrar.lambda$registerBundles$0(AzureKeyVaultSslBundleRegistrar.java:91) ~[spring-cloud-azure-autoconfigure-7.3.0.jar:7.3.0]
at java.base/java.util.HashMap.forEach(HashMap.java:1430) ~[na:na]
at com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.AzureKeyVaultSslBundleRegistrar.registerBundles(AzureKeyVaultSslBundleRegistrar.java:80) ~[spring-cloud-azure-autoconfigure-7.3.0.jar:7.3.0]
at org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration.lambda$sslBundleRegistry$0(SslAutoConfiguration.java:64) ~[spring-boot-autoconfigure-4.0.7.jar:4.0.7]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186) ~[na:na]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) ~[na:na]
at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395) ~[na:na]
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261) ~[na:na]
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:261) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:571) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:632) ~[na:na]
at org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration.sslBundleRegistry(SslAutoConfiguration.java:64) ~[spring-boot-autoconfigure-4.0.7.jar:4.0.7]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:155) ~[spring-beans-7.0.8.jar:7.0.8]
... 55 common frames omitted
Caused by: java.security.KeyStoreException: DKS not found
at java.base/java.security.KeyStore.getInstance(KeyStore.java:926) ~[na:na]
at com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.AzureKeyVaultSslBundleRegistrar.initilizeKeyVaultKeyStore(AzureKeyVaultSslBundleRegistrar.java:152) ~[spring-cloud-azure-autoconfigure-7.3.0.jar:7.3.0]
... 74 common frames omitted
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: DKS, provider: AzureKeyVault, class: com.azure.security.keyvault.jca.KeyVaultKeyStore)
at java.base/java.security.Provider$Service.newInstance(Provider.java:1758) ~[na:na]
at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:227) ~[na:na]
at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:197) ~[na:na]
at java.base/java.security.Security.getImpl(Security.java:762) ~[na:na]
at java.base/java.security.KeyStore.getInstance(KeyStore.java:923) ~[na:na]
... 75 common frames omitted
Caused by: java.lang.RuntimeException: Fail to get response from Key Vault because return http status code is 403. It can be caused by missing permissions or roles. To know how to add permissions or roles, see https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/keyvault/azure-security-keyvault-jca#prerequisites.
at com.azure.security.keyvault.jca.implementation.utils.HttpUtil.lambda$createResponseHandler$0(HttpUtil.java:154) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.shaded.org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:247) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.shaded.org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:188) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.shaded.org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:162) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.utils.HttpUtil.get(HttpUtil.java:73) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.KeyVaultClient.getKey(KeyVaultClient.java:455) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.certificates.KeyVaultCertificates.lambda$refreshCertificates$0(KeyVaultCertificates.java:164) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) ~[na:na]
at com.azure.security.keyvault.jca.implementation.certificates.KeyVaultCertificates.refreshCertificates(KeyVaultCertificates.java:163) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.certificates.KeyVaultCertificates.refreshCertificatesIfNeeded(KeyVaultCertificates.java:147) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.implementation.certificates.KeyVaultCertificates.getAliases(KeyVaultCertificates.java:106) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at com.azure.security.keyvault.jca.KeyVaultKeyStore.<init>(KeyVaultKeyStore.java:151) ~[azure-security-keyvault-jca-2.11.0.jar:2.11.0]
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483) ~[na:na]
at java.base/java.security.Provider$Service.newInstanceOf(Provider.java:1769) ~[na:na]
at java.base/java.security.Provider$Service.newInstanceUtil(Provider.java:1776) ~[na:na]
at java.base/java.security.Provider$Service.newInstance(Provider.java:1751) ~[na:na]
... 79 common frames omitted
To Reproduce
Steps to reproduce the behavior:
-
Create a Key Vault with two enabled self-signed certificates: client-cert-active and client-cert-unused.
-
Configure a Spring SSL bundle that references only client-cert-active (see code snippet below).
-
Start the app — it starts successfully (both certs enabled).
-
Disable the unused certificate:
az keyvault certificate set-attributes --vault-name <vault> --name client-cert-unused --enabled false
-
Start the app again — it fails to start with a 403, even though client-cert-unused is never referenced.
A complete, runnable reproduction (Terraform to provision the vault + two certs, a minimal Spring Boot app, and a disable-cert.sh helper) is available here: https://github.com/vplme/azure-jca-spring-reproduction
Code Snippet
application.yml:
spring:
cloud:
azure:
keyvault:
jca:
vaults:
my-vault:
endpoint: ${AZURE_KEY_VAULT_URI}
ssl:
bundle:
keyvault:
client-bundle:
for-client-auth: true
key:
alias: client-cert-active # the only alias the app needs
keystore:
keyvault-ref: my-vault
Expected behavior
The SSL bundle references client-cert-active only, so the bundle should initialize successfully as long as that alias resolves to a valid, enabled certificate. A disabled certificate elsewhere in the vault should be skipped (and ideally logged at WARN), not cause the whole keystore load to fail.
Screenshots
N/A
Setup (please complete the following information):
- OS: macOS 26.4.1
- IDE: N/A (Maven CLI,
./mvnw spring-boot:run)
- Library/Libraries: com.azure:azure-security-keyvault-jca:2.11.0 (transitive), com.azure.spring:spring-cloud-azure-starter-keyvault-jca:7.3.0, com.azure.spring:spring-cloud-azure-dependencies:7.3.0 (BOM)
- Java version: 21+ (reproduced on 25.0.1)
- App Server/Environment: embedded Tomcat 11 (Spring Boot)
- Frameworks: Spring Boot 4.0.7, Spring Framework 7.0.8
If you suspect a dependency version mismatch (e.g. you see NoClassDefFoundError, NoSuchMethodError or similar), please check out Troubleshoot dependency version conflict article first. If it doesn't provide solution for the problem, please provide:
- verbose dependency tree (
mvn dependency:tree -Dverbose)
- exception message, full stack trace, and any available logs
Additional context
Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report
Describe the bug
When an Azure Key Vault contains multiple certificates and any one of them is disabled, the JCA
KeyStore(AzureKeyVault/DKS) fails to initialize — even when the disabled certificate is not the alias referenced by the application's Spring SSL bundle.KeyVaultCertificates.refreshCertificates()eagerly iterates over every alias in the vault and fetches the key, certificate, and certificate chain for each one. There is no per-certificate error handling, so a403 Forbiddenreturned for a single disabled certificate propagates up, abortsKeyVaultKeyStore.<init>, and prevents the SSL bundle from being created. The Spring Boot application then fails to start.The net effect: one disabled certificate takes down the whole application, regardless of whether that certificate is actually used.
Exception or Stack Trace
To Reproduce
Steps to reproduce the behavior:
Create a Key Vault with two enabled self-signed certificates:
client-cert-activeandclient-cert-unused.Configure a Spring SSL bundle that references only
client-cert-active(see code snippet below).Start the app — it starts successfully (both certs enabled).
Disable the unused certificate:
Start the app again — it fails to start with a
403, even thoughclient-cert-unusedis never referenced.A complete, runnable reproduction (Terraform to provision the vault + two certs, a minimal Spring Boot app, and a
disable-cert.shhelper) is available here: https://github.com/vplme/azure-jca-spring-reproductionCode Snippet
application.yml:Expected behavior
The SSL bundle references
client-cert-activeonly, so the bundle should initialize successfully as long as that alias resolves to a valid, enabled certificate. A disabled certificate elsewhere in the vault should be skipped (and ideally logged atWARN), not cause the whole keystore load to fail.Screenshots
N/A
Setup (please complete the following information):
./mvnw spring-boot:run)If you suspect a dependency version mismatch (e.g. you see
NoClassDefFoundError,NoSuchMethodErroror similar), please check out Troubleshoot dependency version conflict article first. If it doesn't provide solution for the problem, please provide:mvn dependency:tree -Dverbose)Additional context
Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report