Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/versioning/version_client.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ com.azure:azure-security-attestation;1.1.40;1.2.0-beta.1
com.azure:azure-security-confidentialledger;1.0.36;1.1.0-beta.4
com.azure:azure-security-keyvault-administration;4.8.1;4.9.0-beta.2
com.azure:azure-security-keyvault-certificates;4.9.1;4.10.0-beta.2
com.azure:azure-security-keyvault-jca;2.11.0;2.12.0
com.azure:azure-security-keyvault-jca;2.12.0;2.12.1
com.azure:azure-security-test-keyvault-jca;1.0.0;1.0.0
com.azure:azure-security-keyvault-keys;4.11.1;4.12.0-beta.2
com.azure:azure-security-keyvault-secrets;4.11.1;4.12.0-beta.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,53 +238,35 @@ public void invalidHttpPortEnvironmentConfigDefault(String port) {
private static Stream<Arguments> loadFromEnvironmentSupplier() {
return Stream.of(
// Basic Azure HTTPS proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(
new TestConfigurationSource().put(Configuration.PROPERTY_HTTPS_PROXY, AZURE_HTTPS_PROXY_HOST_ONLY)),
PROXY_HOST, 443, null, null, null),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTPS_PROXY_HOST_ONLY, null, true), PROXY_HOST, 443,
null, null, null),

// Username only Azure HTTPS proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(new TestConfigurationSource()
.put(Configuration.PROPERTY_HTTPS_PROXY, AZURE_HTTPS_PROXY_WITH_USERNAME)),
PROXY_HOST, 443, null, null, null),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTPS_PROXY_WITH_USERNAME, null, true), PROXY_HOST,
443, null, null, null),

// Complete Azure HTTPS proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(new TestConfigurationSource()
.put(Configuration.PROPERTY_HTTPS_PROXY, AZURE_HTTPS_PROXY_WITH_USER_AND_PASS)),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTPS_PROXY_WITH_USER_AND_PASS, null, true),
PROXY_HOST, 443, FAKE_PROXY_USER_PLACEHOLDER, FAKE_PROXY_PASSWORD_PLACEHOLDER, null),

// Azure HTTPS proxy with non-proxying hosts.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(
new TestConfigurationSource().put(Configuration.PROPERTY_HTTPS_PROXY, AZURE_HTTPS_PROXY_HOST_ONLY)
.put(Configuration.PROPERTY_NO_PROXY, NON_PROXY_HOSTS)),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTPS_PROXY_HOST_ONLY, NON_PROXY_HOSTS, true),
PROXY_HOST, 443, null, null, "(" + NON_PROXY_HOSTS + ")"),

// Basic Azure HTTP proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(
new TestConfigurationSource().put(Configuration.PROPERTY_HTTP_PROXY, AZURE_HTTP_PROXY_HOST_ONLY)),
PROXY_HOST, 80, null, null, null),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTP_PROXY_HOST_ONLY, null, false), PROXY_HOST, 80,
null, null, null),

// Username only Azure HTTP proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(new TestConfigurationSource()
.put(Configuration.PROPERTY_HTTP_PROXY, AZURE_HTTP_PROXY_WITH_USERNAME)),
PROXY_HOST, 80, null, null, null),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTP_PROXY_WITH_USERNAME, null, false), PROXY_HOST,
80, null, null, null),

// Complete Azure HTTP proxy.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(new TestConfigurationSource()
.put(Configuration.PROPERTY_HTTP_PROXY, AZURE_HTTP_PROXY_WITH_USER_AND_PASS)),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTP_PROXY_WITH_USER_AND_PASS, null, false),
PROXY_HOST, 80, FAKE_PROXY_USER_PLACEHOLDER, FAKE_PROXY_PASSWORD_PLACEHOLDER, null),

// Azure HTTP proxy with non-proxying hosts.
Arguments.of(
setJavaSystemProxyPrerequisiteToTrue(
new TestConfigurationSource().put(Configuration.PROPERTY_HTTP_PROXY, AZURE_HTTP_PROXY_HOST_ONLY)
.put(Configuration.PROPERTY_NO_PROXY, NON_PROXY_HOSTS)),
Arguments.of(setJavaSystemProxyPrerequisiteToTrue(AZURE_HTTP_PROXY_HOST_ONLY, NON_PROXY_HOSTS, false),
PROXY_HOST, 80, null, null, "(" + NON_PROXY_HOSTS + ")"),

/*
Expand Down Expand Up @@ -447,10 +429,8 @@ private static Stream<Arguments> nonProxyHostsSupplier() {
/*
* This emulates loading an environment formatted proxy.
*/
TestConfigurationSource envSource
= new TestConfigurationSource().put(Configuration.PROPERTY_HTTPS_PROXY, "http://localhost:7777")
.put(Configuration.PROPERTY_NO_PROXY, noProxyNonProxyHosts);
Configuration environmentProxyConfiguration = setJavaSystemProxyPrerequisiteToTrue(envSource);
Configuration environmentProxyConfiguration
= setJavaSystemProxyPrerequisiteToTrue("http://localhost:7777", noProxyNonProxyHosts, true);

Pattern environmentProxyConfigurationPattern
= compile(fromConfiguration(environmentProxyConfiguration).getNonProxyHosts(), Pattern.CASE_INSENSITIVE);
Expand Down Expand Up @@ -510,8 +490,22 @@ public void sanitizeJavaHttpNonProxyHostsDoesNotSplitEscapedPipes() {
assertEquals("(" + nonProxyHosts + ")", ProxyOptions.sanitizeNoProxy(nonProxyHosts));
}

private static Configuration setJavaSystemProxyPrerequisiteToTrue(TestConfigurationSource source) {
return new ConfigurationBuilder(EMPTY_SOURCE, EMPTY_SOURCE, source.put(JAVA_SYSTEM_PROXY_PREREQUISITE, "true"))
.build();
private static Configuration setJavaSystemProxyPrerequisiteToTrue(String proxy, String nonProxyHosts,
boolean isHttps) {
TestConfigurationSource testSource = new TestConfigurationSource().put(JAVA_SYSTEM_PROXY_PREREQUISITE, "true")
// Use empty string when non-proxy hosts is null or empty to ensure that the environment configuration
// doesn't attempt to fall back to loading from System.properties or System.env.
.put(Configuration.PROPERTY_NO_PROXY, CoreUtils.isNullOrEmpty(nonProxyHosts) ? "" : nonProxyHosts);

if (isHttps) {
testSource.put(Configuration.PROPERTY_HTTPS_PROXY, proxy);
} else {
testSource.put(Configuration.PROPERTY_HTTP_PROXY, proxy)
// Same as with non-proxy hosts, set HTTPS_PROXY to empty when testing HTTP_PROXY as HTTPS is checked
// first and could fall back to loading from System.properties or System.env.
.put(Configuration.PROPERTY_HTTPS_PROXY, "");
}

return new ConfigurationBuilder(EMPTY_SOURCE, EMPTY_SOURCE, testSource).build();
}
}
5 changes: 5 additions & 0 deletions sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release History

## 2.12.1 (2026-10-17)

### Bugs Fixed
- Preserved configured TLS endpoint identification and connection-specific validation when delegating certificate checks, including checks of certificates trusted through the Key Vault keystore. ([#50744](https://github.com/Azure/azure-sdk-for-java/pull/50744))

## 2.12.0 (2026-07-24)

### Bugs Fixed
Expand Down
2 changes: 1 addition & 1 deletion sdk/keyvault/azure-security-keyvault-jca/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ add the direct dependency to your project as follows.
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-jca</artifactId>
<version>2.12.0</version>
<version>2.12.1</version>
</dependency>
```
[//]: # ({x-version-update-end})
Expand Down
2 changes: 1 addition & 1 deletion sdk/keyvault/azure-security-keyvault-jca/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-jca</artifactId>
<version>2.12.0</version> <!-- {x-version-update;com.azure:azure-security-keyvault-jca;current} -->
<version>2.12.1</version> <!-- {x-version-update;com.azure:azure-security-keyvault-jca;current} -->
<name>JCA Provider for Azure Key Vault</name>
<description>The Java Crypto Architecture (JCA) Provider for Azure Key Vault</description>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

package com.azure.security.keyvault.jca;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.Socket;
import java.security.KeyStore;
Expand Down Expand Up @@ -34,7 +34,7 @@ public final class KeyVaultTrustManager extends X509ExtendedTrustManager {
/**
* Stores the default trust manager.
*/
private X509TrustManager defaultTrustManager;
private final X509ExtendedTrustManager defaultTrustManager;

/**
* Stores the keystore.
Expand All @@ -43,6 +43,8 @@ public final class KeyVaultTrustManager extends X509ExtendedTrustManager {

/**
* Constructor.
*
* @throws IllegalStateException if an extended PKIX trust manager cannot be initialized.
*/
public KeyVaultTrustManager() {
this(null);
Expand All @@ -52,6 +54,7 @@ public KeyVaultTrustManager() {
* Constructor.
*
* @param keyStore the keystore.
* @throws IllegalStateException if an extended PKIX trust manager cannot be initialized.
*/
public KeyVaultTrustManager(KeyStore keyStore) {
this.keyStore = keyStore;
Expand All @@ -64,21 +67,74 @@ public KeyVaultTrustManager(KeyStore keyStore) {
}
}
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
factory.init(keyStore);
defaultTrustManager = (X509TrustManager) factory.getTrustManagers()[0];
} catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException ex) {
LOGGER.log(WARNING, "Unable to get the trust manager factory.", ex);
defaultTrustManager = createTrustManager(keyStore);
} catch (CertificateException ex) {
throw new IllegalStateException("Unable to initialize the trust manager.", ex);
}
if (defaultTrustManager == null) {
}

private static X509ExtendedTrustManager createTrustManager(KeyStore trustStore) throws CertificateException {
CertificateException failure = new CertificateException("Unable to initialize an extended PKIX trust manager.");
for (String provider : new String[] { "SunJSSE", "IbmJSSE" }) {
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX", "IbmJSSE");
factory.init(keyStore);
defaultTrustManager = (X509TrustManager) factory.getTrustManagers()[0];
TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX", provider);
factory.init(trustStore);
for (TrustManager manager : factory.getTrustManagers()) {
if (manager instanceof X509ExtendedTrustManager) {
return (X509ExtendedTrustManager) manager;
}
}
failure
.addSuppressed(new CertificateException(provider + " does not supply an extended trust manager."));
} catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException ex) {
LOGGER.log(WARNING, "Unable to get the trust manager factory.", ex);
failure.addSuppressed(ex);
}
}
throw failure;
}

private void checkTrusted(X509Certificate[] chain, TrustCheck check) throws CertificateException {
CertificateException originalFailure;
try {
check.check(defaultTrustManager);
return;
} catch (CertificateException ex) {
originalFailure = ex;
}
if (keyStore == null) {
throw originalFailure;
}
X509ExtendedTrustManager fallback;
try {
if (keyStore.getCertificateAlias(chain[0]) == null) {
throw originalFailure;
}
// A matching Key Vault leaf is a trust anchor, not an exemption from connection-specific checks.
KeyStore trustedCertificate = KeyStore.getInstance("JKS");
trustedCertificate.load(null, null);
trustedCertificate.setCertificateEntry("trusted", chain[0]);
fallback = createTrustManager(trustedCertificate);
} catch (KeyStoreException | IOException | NoSuchAlgorithmException ex) {
CertificateException failure = new CertificateException("Unable to verify in keystore.", ex);
failure.addSuppressed(originalFailure);
throw failure;
} catch (CertificateException failure) {
if (failure != originalFailure) {
failure.addSuppressed(originalFailure);
}
throw failure;
}
try {
check.check(fallback);
} catch (CertificateException failure) {
failure.addSuppressed(originalFailure);
throw failure;
}
}

@FunctionalInterface
private interface TrustCheck {
void check(X509ExtendedTrustManager manager) throws CertificateException;
}

/**
Expand Down Expand Up @@ -179,7 +235,7 @@ public X509Certificate[] getAcceptedIssuers() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
checkClientTrusted(chain, authType);
checkTrusted(chain, manager -> manager.checkClientTrusted(chain, authType, socket));
}

/**
Expand All @@ -194,7 +250,7 @@ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
checkServerTrusted(chain, authType);
checkTrusted(chain, manager -> manager.checkServerTrusted(chain, authType, socket));
}

/**
Expand All @@ -209,7 +265,7 @@ public void checkServerTrusted(X509Certificate[] chain, String authType, Socket
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
checkClientTrusted(chain, authType);
checkTrusted(chain, manager -> manager.checkClientTrusted(chain, authType, engine));
}

/**
Expand All @@ -224,6 +280,6 @@ public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngi
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
checkServerTrusted(chain, authType);
checkTrusted(chain, manager -> manager.checkServerTrusted(chain, authType, engine));
}
}
Loading
Loading