diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index f54a01d8607d..2a1a99ff3300 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -13,6 +13,10 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. - Add ConnectionDetails for ServiceBus. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). - Add ConnectionDetails for EventHubs. [#47926](https://github.com/Azure/azure-sdk-for-java/pull/47926). +#### Bugs Fixed + +- Fixed `KeyVaultJcaProvider` being registered as the highest-priority JCA security provider, which overrides standard JCA services (`KeyManagerFactory.SunX509`, `Signature` algorithms) and breaks mTLS with standard keystores (JKS, PKCS12). The provider is now added at the end of the provider list, allowing JCA's delayed provider selection to route `KeyVaultPrivateKey` signing operations to the KeyVault implementations without interfering with standard SSL/TLS operations. [#48183](https://github.com/Azure/azure-sdk-for-java/issues/48183) + ### Spring Cloud Azure Docker Compose This section includes changes in `spring-cloud-azure-docker-compose` module. diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrar.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrar.java index 2a94664e3db6..10521f66f533 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrar.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrar.java @@ -137,7 +137,7 @@ private KeyStore initilizeKeyVaultKeyStore(String storeName, configureJcaKeyStoreSystemProperties(jcaVaultProperties, keyStoreProperties, resourceLoader); if (providerConfigured.compareAndSet(false, true)) { Security.removeProvider(KeyVaultJcaProvider.PROVIDER_NAME); - Security.insertProviderAt(new KeyVaultJcaProvider(), 1); + Security.addProvider(new KeyVaultJcaProvider()); } KeyStore azureKeyVaultKeyStore; try { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java index ba2539ab7c9c..2d1a99583046 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java @@ -3,8 +3,10 @@ package com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca; +import com.azure.security.keyvault.jca.KeyVaultJcaProvider; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.properties.AzureKeyVaultJcaProperties; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.jca.properties.AzureKeyVaultSslBundleProperties; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Isolated; @@ -19,6 +21,7 @@ import org.springframework.util.ClassUtils; import java.security.KeyStore; +import java.security.Security; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -33,6 +36,11 @@ @ExtendWith({OutputCaptureExtension.class}) class AzureKeyVaultSslBundleRegistrarTests { + @AfterEach + void cleanupProvider() { + Security.removeProvider(KeyVaultJcaProvider.PROVIDER_NAME); + } + @Test void noJcaProviderOnClassPath(CapturedOutput capturedOutput) { AzureKeyVaultSslBundleRegistrar registrar = new AzureKeyVaultSslBundleRegistrar(new AzureKeyVaultJcaProperties(), new AzureKeyVaultSslBundleProperties()); @@ -200,4 +208,42 @@ void registerMultipleSslBundles(CapturedOutput capturedOutput) { }); } } + + @Test + void keyVaultProviderNotInsertedAtHighestPriority() { + AzureKeyVaultJcaProperties jcaProperties = new AzureKeyVaultJcaProperties(); + AzureKeyVaultSslBundleProperties sslBundleProperties = new AzureKeyVaultSslBundleProperties(); + AzureKeyVaultSslBundleRegistrar registrar = new AzureKeyVaultSslBundleRegistrar(jcaProperties, sslBundleProperties); + registrar.setResourceLoader(new DefaultResourceLoader()); + SslBundleRegistry registry = Mockito.mock(SslBundleRegistry.class); + + try (MockedStatic keyStoreMockedStatic = mockStatic(KeyStore.class)) { + KeyStore keyStore = Mockito.mock(KeyStore.class); + keyStoreMockedStatic.when(() -> KeyStore.getInstance(KeyVaultJcaProvider.PROVIDER_NAME)).thenReturn(keyStore); + + String keyvaultName = "keyvault1"; + AzureKeyVaultJcaProperties.JcaVaultProperties jcaVaultProperties = new AzureKeyVaultJcaProperties.JcaVaultProperties(); + jcaVaultProperties.setEndpoint("https://test.vault.azure.net/"); + jcaProperties.getVaults().put(keyvaultName, jcaVaultProperties); + AzureKeyVaultSslBundleProperties.KeyVaultSslBundleProperties bundleProperties = new AzureKeyVaultSslBundleProperties.KeyVaultSslBundleProperties(); + bundleProperties.getTruststore().setKeyvaultRef(keyvaultName); + sslBundleProperties.getKeyvault().put("testBundle", bundleProperties); + + registrar.registerBundles(registry); + + // Verify the KeyVault JCA provider is NOT inserted at position 1 (highest priority), + // to avoid interfering with standard SSL/TLS operations and breaking mTLS. + java.security.Provider[] providers = Security.getProviders(); + int providerPosition = -1; + for (int i = 0; i < providers.length; i++) { + if (KeyVaultJcaProvider.PROVIDER_NAME.equals(providers[i].getName())) { + providerPosition = i + 1; // 1-indexed position + break; + } + } + assertTrue(providerPosition > 1, "KeyVaultJcaProvider must not be inserted at position 1 " + + "to avoid overriding standard JCA services like KeyManagerFactory.SunX509 and Signature algorithms, " + + "which would break mTLS with standard keystores."); + } + } }