Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6c8ec90
Add configured certificate filtering and lazy loading in KeyVault JCA
rujche Jul 10, 2026
86621f2
Add PR link to keyvault-jca changelog entry
rujche Jul 10, 2026
f4371a4
Address PR review comments for alias refresh and thread safety
rujche Jul 10, 2026
5507d46
Address additional PR review feedback on thread safety and refresh be…
rujche Jul 10, 2026
cc494aa
Make alias lookup use snapshots for thread-safe iteration
rujche Jul 10, 2026
b3cd9d5
Ensure refreshed aliases list remains mutable
rujche Jul 10, 2026
7875700
Restore key vault precedence over classpath in keystore lookups
rujche Jul 10, 2026
a343059
Refine lazy key vault loads and remove brittle reflection test
rujche Jul 10, 2026
a6fbf69
Fix import order and remove unused import
rujche Jul 10, 2026
9d74fac
Strengthen test isolation and list API assertions
rujche Jul 10, 2026
162a828
Retry certificate load when Key Vault returns null
rujche Jul 10, 2026
d588c4d
Suppress GoodLoggingCheck and fix synchronization consistency
rujche Jul 10, 2026
b4aae28
Retry key and chain loads after transient failures
rujche Jul 10, 2026
c553796
Use lazy warning logs and remove extra suppression
rujche Jul 10, 2026
c73eb95
Reset key vault certificate cache when client changes
rujche Jul 10, 2026
72887a0
Make key vault client update atomic and reuse cache reset
rujche Jul 10, 2026
ce99b3e
Ignore null configured certificate aliases
rujche Jul 10, 2026
7ec6baf
Use allCertificates precedence in KeyVaultKeyStore lookups
rujche Jul 10, 2026
f34921f
Add GoodLoggingCheck suppression for KeyVaultCertificates
rujche Jul 10, 2026
9506d26
Guard null certificate alias lookup and clarify refresh docs
rujche Jul 10, 2026
35885e3
Reduce lock contention in lazy certificate loading
rujche Jul 13, 2026
e67607c
Add certificates-filter-patterns property for alias regex filtering
rujche Jul 13, 2026
8cddbf0
Simplify certificates filter pattern README wording
rujche Jul 13, 2026
f1ce384
Align changelog filter pattern wording with README
rujche Jul 13, 2026
470b184
Rename certificate alias filter property
rujche Jul 13, 2026
524655e
Document default alias filter behavior
rujche Jul 13, 2026
7662126
Reorder default filter behavior sentence in README
rujche Jul 13, 2026
60907c2
Fix stale-client publish races in lazy loaders
rujche Jul 13, 2026
d75b0e2
Regenerate linting suppressions for keyvault-jca
rujche Jul 13, 2026
b3120a8
Address review feedback on alias logging and filter parsing
rujche Jul 13, 2026
91a495c
Address review feedback on filter pattern normalization
rujche Jul 13, 2026
a407a55
Refine alias filter error message and state usage
rujche Jul 13, 2026
31c94e2
Harden certificate lookup null safety and key map exposure
rujche Jul 13, 2026
fb84762
Defensively copy exposed certificate collections
rujche Jul 13, 2026
e450841
Refine KeyVault certificate refresh locking
rujche Jul 13, 2026
7c23284
Avoid duplicate certificate chain cloning
rujche Jul 13, 2026
207c42d
Fix out-of-order alias writes on concurrent refresh
moarychan Aug 3, 2026
4479907
Configure each alias filter as its own property
moarychan Aug 3, 2026
15c6dcb
Merge remote-tracking branch 'origin/main' into pr-49774
moarychan Aug 3, 2026
80c191d
Clarify Key Vault alias filter documentation
moarychan Aug 6, 2026
a06596d
Prevent duplicate and stale Key Vault material loads
moarychan Aug 6, 2026
8be2fbe
Pin lazy-loaded material to one certificate version
moarychan Aug 6, 2026
4a60dc0
Preserve Key Vault material load failures
moarychan Aug 7, 2026
3f5a8d0
Merge remote-tracking branch 'origin/main' into pr-49774
moarychan Aug 7, 2026
ad136bb
Stabilize concurrent failure tests
moarychan Aug 7, 2026
e187f16
Propagate certificate chain decoding failures
moarychan Aug 11, 2026
12d9c9c
Merge remote-tracking branch 'origin/main' into pr-49774
moarychan Aug 11, 2026
6a4f210
Distinguish nested single-flight test waiters
moarychan Aug 11, 2026
807eeb0
Reject malformed certificate PEM blocks
moarychan Aug 14, 2026
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: 2 additions & 0 deletions sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 2.12.0-beta.1 (Unreleased)

### Features Added
- Added lazy loading for Key Vault certificate details in the JCA keystore. Certificate details are now loaded by alias when requested, avoiding unnecessary reads for unconfigured certificates. ([#49774](https://github.com/Azure/azure-sdk-for-java/pull/49774))
- Added support for `azure.keyvault.jca.certificates` to specify a comma-separated list of Key Vault certificate aliases to load. When set, only these aliases are considered from Key Vault. ([#39487](https://github.com/Azure/azure-sdk-for-java/issues/39487))

### Breaking Changes

Expand Down
1 change: 1 addition & 0 deletions sdk/keyvault/azure-security-keyvault-jca/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ The JCA library supports configuring the following options:
* `azure.keyvault.jca.refresh-certificates-when-have-un-trust-certificate`: Indicates whether to refresh certificates when have untrusted certificate.
* `azure.keyvault.jca.certificates-refresh-interval`: The refresh interval time.
* `azure.keyvault.jca.certificates-refresh-interval-in-ms`: The refresh interval time.
Comment thread
rujche marked this conversation as resolved.
* `azure.keyvault.jca.certificates`: Comma-separated certificate aliases to load from Key Vault. When set, only these aliases are considered from Key Vault.
* `azure.keyvault.disable-challenge-resource-verification`: Indicates whether to disable verification that the authentication challenge resource matches the Key Vault or Managed HSM domain.

You can configure these properties using:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<suppress files="com.azure.security.keyvault.jca.implementation.KeyVaultClient.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
<suppress files="com.azure.security.keyvault.jca.implementation.certificates.ClasspathCertificates.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
<suppress files="com.azure.security.keyvault.jca.implementation.certificates.JreCertificates.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
<suppress files="com.azure.security.keyvault.jca.implementation.certificates.KeyVaultCertificates.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
<suppress files="com.azure.security.keyvault.jca.implementation.certificates.SpecificPathCertificates.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
Comment thread
rujche marked this conversation as resolved.
Comment thread
rujche marked this conversation as resolved.
Comment thread
rujche marked this conversation as resolved.
<suppress files="com.azure.security.keyvault.jca.implementation.utils.AccessTokenUtil.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
<suppress files="com.azure.security.keyvault.jca.implementation.utils.HttpUtil.java" checks="io.clientcore.linting.extensions.checkstyle.checks.GoodLoggingCheck" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.logging.Level.FINE;
Expand All @@ -56,6 +58,8 @@ public final class KeyVaultKeyStore extends KeyStoreSpi {
*/
private static final Logger LOGGER = Logger.getLogger(KeyVaultKeyStore.class.getName());

static final String CONFIGURED_CERTIFICATES_PROPERTY = "azure.keyvault.jca.certificates";

/**
* Stores the Jre key store certificates.
*/
Expand Down Expand Up @@ -146,8 +150,9 @@ public KeyVaultKeyStore() {
customCertificates = SpecificPathCertificates.getSpecificPathCertificates(customPath);
LOGGER.log(FINE, String.format("Loaded custom certificates: %s.", customCertificates.getAliases()));

keyVaultCertificates = new KeyVaultCertificates(refreshInterval, keyVaultUri, tenantId, clientId, clientSecret,
managedIdentity, accessToken, disableChallengeResourceVerification);
keyVaultCertificates
= new KeyVaultCertificates(refreshInterval, keyVaultUri, tenantId, clientId, clientSecret, managedIdentity,
accessToken, disableChallengeResourceVerification, getConfiguredKeyVaultCertificateAliases());
LOGGER.log(FINE, String.format("Loaded Key Vault certificates: %s.", keyVaultCertificates.getAliases()));
Comment thread
rujche marked this conversation as resolved.
Outdated

classpathCertificates = new ClasspathCertificates();
Expand All @@ -168,6 +173,15 @@ Long getRefreshInterval() {
.orElse(0L);
}

Set<String> getConfiguredKeyVaultCertificateAliases() {
return Optional.ofNullable(System.getProperty(CONFIGURED_CERTIFICATES_PROPERTY))
.map(value -> Stream.of(value.split(","))
Comment thread
moarychan marked this conversation as resolved.
Outdated
.map(String::trim)
.filter(alias -> !alias.isEmpty())
.collect(Collectors.toSet()))
.orElse(Collections.emptySet());
}

/**
* get key vault key store by system property
*
Expand Down Expand Up @@ -254,16 +268,22 @@ public boolean engineEntryInstanceOf(String alias, Class<? extends KeyStore.Entr
*/
@Override
public Certificate engineGetCertificate(String alias) {
Certificate certificate = allCertificates.stream()
.map(AzureCertificates::getCertificates)
.filter(a -> a.containsKey(alias))
.findFirst()
.map(certificates -> certificates.get(alias))
.orElse(null);
Certificate certificate = null;
for (AzureCertificates certificatesSource : allCertificates) {
if (certificatesSource instanceof KeyVaultCertificates) {
certificate = ((KeyVaultCertificates) certificatesSource).getCertificate(alias);
} else {
certificate = certificatesSource.getCertificates().get(alias);
}

if (certificate != null) {
break;
}
}

if (refreshCertificatesWhenHaveUnTrustCertificate && certificate == null) {
keyVaultCertificates.refreshCertificates();
certificate = keyVaultCertificates.getCertificates().get(alias);
certificate = keyVaultCertificates.getCertificate(alias);
}
Comment thread
rujche marked this conversation as resolved.

return certificate;
Expand Down Expand Up @@ -307,16 +327,22 @@ public String engineGetCertificateAlias(Certificate cert) {
*/
@Override
public Certificate[] engineGetCertificateChain(String alias) {
Certificate[] certificates = allCertificates.stream()
.map(AzureCertificates::getCertificateChains)
.filter(Objects::nonNull)
.filter(a -> a.containsKey(alias))
.findFirst()
.map(m -> m.get(alias))
.orElse(null);
Certificate[] certificates = null;
for (AzureCertificates certificatesSource : allCertificates) {
if (certificatesSource instanceof KeyVaultCertificates) {
certificates = ((KeyVaultCertificates) certificatesSource).getCertificateChain(alias);
} else {
certificates = certificatesSource.getCertificateChains().get(alias);
}

if (certificates != null) {
break;
}
}

if (refreshCertificatesWhenHaveUnTrustCertificate && certificates == null) {
keyVaultCertificates.refreshCertificates();
return keyVaultCertificates.getCertificateChains().get(alias);
return keyVaultCertificates.getCertificateChain(alias);
}
return certificates;
Comment thread
rujche marked this conversation as resolved.
}
Comment thread
rujche marked this conversation as resolved.
Expand Down Expand Up @@ -358,12 +384,20 @@ public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter
*/
@Override
public Key engineGetKey(String alias, char[] password) {
return allCertificates.stream()
.map(AzureCertificates::getCertificateKeys)
.filter(a -> a.containsKey(alias))
.findFirst()
.map(certificateKeys -> certificateKeys.get(alias))
.orElse(null);
Key key = null;
for (AzureCertificates certificatesSource : allCertificates) {
if (certificatesSource instanceof KeyVaultCertificates) {
key = ((KeyVaultCertificates) certificatesSource).getCertificateKey(alias);
} else {
key = certificatesSource.getCertificateKeys().get(alias);
}

if (key != null) {
break;
}
}

return key;
}

/**
Expand Down
Loading
Loading