Skip to content

[BUG] Adding Keyvault-based Spring SSL bundle breaks mTLS #48183

Closed

Description

I have a Spring Boot 3.5.8 app running on Java 25 on Azure App Service. It depends on Spring Cloud Azure, using the 6.1.0 BOM.
In the app we have an Apache Components HTTP Clients configured to use mTLS to talk to a SOAP service. When we configure the client to use a key and certificate from an SSL Bundle backed by a local .p12 keystore, this works as expected.
However, as soon as we use a KeyVault-based bundle, this breaks: when it's the bundle to use for the mTLS connection, but also even when the KeyVault-based bundle is simply added next to the JKS bundle without being used.
This typically breaks by throwing a java.net.SocketException: Connection reset by peer or java.net.SocketException: Broken pipe.

To Reproduce

I wrote a standalone Java app as a single source file next to our web app for quick testing on the Azure box from an SSH shell. It looks like this:

package nl;

import nl.aon.libs.schedulerlock.config.ShedlockAutoConfiguration;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.ClientHttpRequestMessageSender;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;

import java.security.Provider;
import java.security.Security;
import java.util.List;

@SpringBootConfiguration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, ShedlockAutoConfiguration.class, MetricsAutoConfiguration.class})
public class Client {
    public static void main(String[] args) throws Exception {
        SpringApplication springApplication = new SpringApplication(Client.class);
        springApplication.setWebApplicationType(WebApplicationType.NONE);
        springApplication.run(args);
    }

    @Bean
    WebServiceTemplate wsTemplate(SslBundles sslBundles) {
        SslBundle audaScan = sslBundles.getBundle("audascanBundle");
        var tlsStrategy = ClientTlsStrategyBuilder.create()
                .setSslContext(audaScan.createSslContext())
                .buildClassic();
        var connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                .setTlsSocketStrategy(tlsStrategy)
                .build();
        var httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .build();
        var webServiceMessageSender = new ClientHttpRequestMessageSender(new HttpComponentsClientHttpRequestFactory(httpClient));
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setMessageSender(webServiceMessageSender);
        return webServiceTemplate;
    }

    @Bean
    CommandLineRunner call(WebServiceTemplate webServiceTemplate) {
        return args -> {
            Provider[] providers = Security.getProviders();
            System.out.println("providers = " + List.of(providers));

            System.out.println("Calling WS");
            var result = new StringResult();
            webServiceTemplate.sendSourceAndReceiveToResult("https://isa-ws.dem.solera.nl/isa-ws/services/isa3", new StringSource("""
                            <ns2:AUDASCAN-360-V2 xmlns:ns2="http://www.abz.nl/schema/audascan" xmlns:ns3="http://www.abz.nl/schema/isa" xmlns:ns4="http://www.abz.nl/schema/foto" xmlns:ns5="urn:isa3"><ns3:VT><ns3:VT_KENTEKEN>GHT69D</ns3:VT_KENTEKEN><ns3:VT_MELDCODE>1010</ns3:VT_MELDCODE></ns3:VT></ns2:AUDASCAN-360-V2>
                            """),
                    result);
            System.out.println("result = " + result);
        };
    }
}

and I've added the following application.yml config file:

spring:
  cloud:
    azure:
      keyvault:
        jca:
          vaults:
            keyvault1:
              endpoint: https://kv-[redacted].vault.azure.net/
              credential:
                client-id: [redacted]
                managed-identity-enabled: true
---
spring:
  config.activate.on-profile: p12-solo
  ssl:
    bundle:
      jks:
        audascanBundle:
          keystore:
            location: classpath:audascan.p12
            password: changeit
---
spring:
  config.activate.on-profile: p12-duo
  ssl:
    bundle:
      jks:
        audascanBundle:
          keystore:
            location: classpath:audascan.p12
            password: changeit
      keyvault:
        audascanBundleKV:
          for-client-auth: true
          keystore:
            keyvault-ref: keyvault1
          key:
            alias: audascan
---
spring:
  config.activate.on-profile: keyvault
  ssl:
    bundle:
      keyvault:
        audascanBundle:
          for-client-auth: true
          keystore:
            keyvault-ref: keyvault1
          key:
            alias: audascan

On the local filesystem we have the audascan.p12 file, which is the same keystore that's also present in the Azure KeyVault under the alias audascan.

I can now run the app in different way. If I start it like this, it works as expected:

java -cp "/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/*" -Dspring.profiles.active=p12-solo nl/Client.java

When run like this, this is what it prints for the security providers:

providers = [SUN version 25, SunRsaSign version 25, SunEC version 25, SunJSSE version 25, SunJCE version 25, SunJGSS version 25, SunSASL version 25, XMLDSig version 25, SunPCSC version 25, JdkLDAP version 25, JdkSASL version 25, SunPKCS11 version 25]

Now, I can also run it like this which will add a KeyVault-based SSL Bundle to the configured list of bundles, without actually using it:

java -cp "/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/*" -Dspring.profiles.active=p12-duo nl/Client.java

This breaks the application. The output for the providers now shows this (which I think is relevant, therefore the code prints this):

providers = [AzureKeyVault version 1.0, SUN version 25, SunRsaSign version 25, SunEC version 25, SunJSSE version 25, SunJCE version 25, SunJGSS version 25, SunSASL version 25, XMLDSig version 25, SunPCSC version 25, JdkLDAP version 25, JdkSASL version 25, SunPKCS11 version 25]

I've read somewhere that some versions of the JCA integration do not support mTLS: however, a) I'm not clear on whether this applies to my version and b) even if it does, I would not expect that simply configuring an additional KeyVault-backed SSL bundle completely breaks my application that doesn't even use that bundle for its mTLS config.

If I run the app by passing the keyvault profile, which enables only the KeyVault-backed SSL bundle, I get the same behavior as with the p12-duo profile.

Is this indeed a bug? Or is it still expected that simply adding a KeyVault-backed SSL bundle will completely break any form of mTLS in my application, even when I configure my own keystores for that? I also tried to rewrite my code to not even use a Spring SSL Bundle but to read the .p12 from the filesystem as a Keystore myself and create the SSL context from that, but it behaves in the same way. Therefore I suspect that this is related to the Azure security provider that gets registered as soon as you add a KeyVault bundle.

Activity

  1. added
    azure-springAll azure-spring related issues
    ClientThis issue points to a problem in the data-plane of the library.
    customer-reportedIssues that are reported by GitHub users external to the Azure organization.
    questionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that
    on Mar 2, 2026
  2. github-actions commented on Mar 2, 2026

    @github-actions
    Contributor

    Thank you for your feedback. Tagging and routing to the team member best able to assist.

  3. changed the title [-][BUG] Adding Spring SSL Keyvault-based bundle brakes mTLS[/-] [+][BUG] Adding Spring SSL Keyvault-based bundle breaks mTLS[/+] on Mar 2, 2026
  4. changed the title [-][BUG] Adding Spring SSL Keyvault-based bundle breaks mTLS[/-] [+][BUG] Adding Keyvault-based Spring SSL bundle breaks mTLS[/+] on Mar 2, 2026
  5. added this to the 2026-03 milestone on Mar 3, 2026
  6. added a commit that references this issue on Mar 3, 2026
  7. jkuipers commented on Apr 20, 2026

    @jkuipers
    Author

    Seems like this wasn't backported to the 6.x branch, so there's no patch for use with Boot 3.5.x.
    Can this be backported? It's a one-line fix, so it's trivial to do.

  8. rujche commented on Apr 22, 2026

    @rujche
    Contributor

    Hi, Joris Kuipers (@jkuipers) , this bug will be fixed in 6.3.0 which is planed to be released before the end of May.

  9. locked and limited conversation to collaborators on Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

ClientThis issue points to a problem in the data-plane of the library.azure-springAll azure-spring related issuescustomer-reportedIssues that are reported by GitHub users external to the Azure organization.questionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

Type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions