diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 99015c9adcd34..1bd7ba7110690 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -186,7 +186,7 @@ com.azure:azure-security-attestation;1.2.0;1.3.0-beta.1 com.azure:azure-security-confidentialledger;1.0.38;2.0.2-beta.2 com.azure:azure-security-keyvault-administration;4.8.3;4.9.0-beta.2 com.azure:azure-security-keyvault-certificates;4.9.3;4.10.0-beta.2 -com.azure:azure-security-keyvault-jca;2.12.0;2.13.0-beta.1 +com.azure:azure-security-keyvault-jca;2.13.0;2.14.0-beta.1 com.azure:azure-security-test-keyvault-jca;1.0.0;1.0.0 com.azure:azure-security-keyvault-keys;4.11.3;4.12.0-beta.3 com.azure:azure-security-keyvault-secrets;4.11.3;4.12.0-beta.1 diff --git a/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md index b7fe6e436de21..11c6b6f4b27ac 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md +++ b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md @@ -1,12 +1,24 @@ # Release History -## 2.13.0-beta.1 (Unreleased) +## 2.14.0-beta.1 (Unreleased) ### Features Added + +### Breaking Changes + +### Bugs Fixed + +### Other Changes + +## 2.13.0 (2026-10-13) + +### Features Added +- Added ISO-8601 duration support (e.g., `PT30S`) for `azure.keyvault.jca.certificates-refresh-interval`, while retaining support for millisecond values. ([#50785](https://github.com/Azure/azure-sdk-for-java/pull/50785)) - 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.certificate-alias-filter-pattern` to filter Key Vault certificate aliases with inclusion/exclusion regex patterns. Inclusion patterns are configured directly and exclusion patterns are prefixed with `!`. Configure more than one filter by appending a suffix to the property name, such as `azure.keyvault.jca.certificate-alias-filter-pattern.1`. A pattern can contain any character as long as the regex is correct. If no filter property is configured, alias filtering is disabled and all discovered Key Vault aliases remain eligible for lazy loading. ([#39487](https://github.com/Azure/azure-sdk-for-java/issues/39487)) ### Breaking Changes +- Removed support for `azure.keyvault.jca.certificates-refresh-interval-in-ms`. ([#50785](https://github.com/Azure/azure-sdk-for-java/pull/50785)) ### 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. ([#50667](https://github.com/Azure/azure-sdk-for-java/pull/50667)) @@ -15,6 +27,7 @@ - Fixed an issue where `KeyStore.load(KeyVaultLoadStoreParameter)` could combine explicit client settings with certificate cache and path settings captured earlier from system properties. The parameter now carries the complete key store configuration. ([#50163](https://github.com/Azure/azure-sdk-for-java/pull/50163)) ### Other Changes +- Changed the certificate refresh interval in `KeyVaultLoadStoreParameter` from `long` milliseconds to `Duration`. ([#50785](https://github.com/Azure/azure-sdk-for-java/pull/50785)) - Added system property `azure.keyvault.jca.disable-aia-download` to disable automatic AIA chain completion. AIA chain completion downloads certificates from URLs embedded in certificate extensions, so this allows locked-down environments to prevent those outbound HTTP(S) requests, mitigating potential SSRF-like attack vectors when loading untrusted certificates. The value is captured when each Key Vault client is initialized and retained for lazy certificate-chain loading, so multiple keystores can use different settings without overwriting one another. Set to `true` to disable (defaults to `false`). - Added `KeyVaultJcaPropertyNames` as the central source for the system property names supported by the Azure Key Vault JCA provider. ([#50163](https://github.com/Azure/azure-sdk-for-java/pull/50163)) - Replaced Apache HttpClient 5 with the JDK `HttpURLConnection`, removing the Apache HttpClient and SLF4J runtime dependencies while preserving strict HTTPS hostname verification, non-fatal AIA download failures, fail-closed TLS trust initialization, and automatic gzip and deflate response decompression. VM managed identity requests to IMDS use `Proxy.NO_PROXY` to bypass HTTP proxy routing. Other requests, including App Service and Container Apps managed identity requests, retain JDK proxy selection. diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md index 6507a00d96f79..d97828ba2ed0a 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/README.md +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -64,7 +64,7 @@ add the direct dependency to your project as follows. com.azure azure-security-keyvault-jca - 2.12.0 + 2.13.0 ``` [//]: # ({x-version-update-end}) @@ -139,8 +139,7 @@ The JCA library supports configuring the following options: * `azure.cert-path.well-known`: The path where the well-known certificate is stored. * `azure.cert-path.custom`: The path where the custom certificate is stored. * `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. +* `azure.keyvault.jca.certificates-refresh-interval`: The certificate refresh interval as an ISO-8601 duration, for example `-Dazure.keyvault.jca.certificates-refresh-interval=PT30S` for 30 seconds. Millisecond values such as `-Dazure.keyvault.jca.certificates-refresh-interval=30000` remain supported for backward compatibility. Defaults to zero; zero or negative values disable periodic refresh. * `azure.keyvault.jca.certificate-alias-filter-pattern`: A regex that filters which Key Vault certificate aliases are eligible for lazy loading. Append a suffix to the property name to configure more than one filter, for example `azure.keyvault.jca.certificate-alias-filter-pattern.1` or `azure.keyvault.jca.certificate-alias-filter-pattern.prod`. If no such property is configured, all discovered Key Vault aliases are eligible for lazy loading. See "Filtering Key Vault certificate aliases" below. * `azure.keyvault.disable-challenge-resource-verification`: Indicates whether to disable verification that the authentication challenge resource matches the Key Vault or Managed HSM domain. * `azure.keyvault.jca.disable-aia-download`: Set to `true` to disable automatic AIA (Authority Information Access) certificate chain completion. Chain completion is only attempted when the chain returned by Azure Key Vault is incomplete, meaning it holds a single certificate or is missing an intermediate CA. When disabled, the provider will return certificate chains as provided by Azure Key Vault without downloading missing intermediate CA certificates. Use this in locked-down environments or when processing untrusted certificates to prevent outbound HTTP(S) requests to URLs embedded in certificate extensions. Defaults to `false` for backward compatibility. The value is captured when a Key Vault keystore and its client are initialized. @@ -154,6 +153,10 @@ or as a JVM argument: -Dazure.keyvault.uri= ``` +The system property `azure.keyvault.jca.certificates-refresh-interval-in-ms` has been removed and is ignored. If it is +configured, the provider logs a warning. Use `azure.keyvault.jca.certificates-refresh-interval` instead, with an ISO-8601 +duration such as `PT30S` or the existing millisecond value. + #### Programmatic configuration Use `KeyVaultLoadStoreParameter` when each key store needs an explicit configuration instead of global system @@ -165,7 +168,7 @@ KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter( "", "", "") - .setCertificatesRefreshIntervalInMs(60_000) + .setCertificatesRefreshInterval(Duration.ofSeconds(60)) .setCertificateAliasFilterPatterns(Collections.singleton("^prod-.*")); parameter.disableAiaDownload(); diff --git a/sdk/keyvault/azure-security-keyvault-jca/pom.xml b/sdk/keyvault/azure-security-keyvault-jca/pom.xml index 0f439a0aa1db3..331a85eb3ffb3 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-jca/pom.xml @@ -13,7 +13,7 @@ com.azure azure-security-keyvault-jca - 2.13.0-beta.1 + 2.14.0-beta.1 JCA Provider for Azure Key Vault The Java Crypto Architecture (JCA) Provider for Azure Key Vault diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaPropertyNames.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaPropertyNames.java index 73f0db5f5c216..cfafa8d020f0d 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaPropertyNames.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultJcaPropertyNames.java @@ -58,17 +58,12 @@ private KeyVaultJcaPropertyNames() { public static final String CERT_PATH_CUSTOM = "azure.cert-path.custom"; /** - * The certificate refresh interval property name. + * The certificate refresh interval property name. Accepts an ISO-8601 duration, such as {@code PT30S}, or a + * millisecond value, such as {@code 30000}. */ public static final String KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL = "azure.keyvault.jca.certificates-refresh-interval"; - /** - * The certificate refresh interval in milliseconds property name. - */ - public static final String KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS - = "azure.keyvault.jca.certificates-refresh-interval-in-ms"; - /** * The property name used to refresh certificates when an untrusted certificate is encountered. */ diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java index c375f26d44cb6..07005a8e25afb 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java @@ -53,6 +53,9 @@ public final class KeyVaultKeyStore extends KeyStoreSpi { */ private static final Logger LOGGER = Logger.getLogger(KeyVaultKeyStore.class.getName()); + private static final String REMOVED_CERTIFICATES_REFRESH_INTERVAL_IN_MS + = "azure.keyvault.jca.certificates-refresh-interval-in-ms"; + /** * Stores the Jre key store certificates. */ @@ -123,6 +126,17 @@ public KeyVaultKeyStore() { updateKeyStoreConfiguration(parameter); } + static void warnIfRemovedCertificatesRefreshIntervalIsConfigured() { + if (System.getProperty(REMOVED_CERTIFICATES_REFRESH_INTERVAL_IN_MS) != null) { + LOGGER.log(WARNING, + "System property {0} has been removed and is ignored. Use {1} instead with an ISO-8601 duration" + + " (for example, PT30S). Millisecond values (for example, 30000) are also supported.", + new Object[] { + REMOVED_CERTIFICATES_REFRESH_INTERVAL_IN_MS, + KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL }); + } + } + private void updateKeyStoreConfiguration(KeyVaultLoadStoreParameter parameter) { refreshCertificatesWhenHaveUnTrustCertificate = parameter.isRefreshCertificatesWhenHaveUnTrustCertificate(); certPathWellKnown = parameter.getCertPathWellKnown(); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java index ae43595544a61..5d4164450301d 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java @@ -4,6 +4,7 @@ package com.azure.security.keyvault.jca; import java.security.KeyStore; +import java.time.Duration; import java.util.Collections; import java.util.HashSet; import java.util.Objects; @@ -60,9 +61,9 @@ public final class KeyVaultLoadStoreParameter implements KeyStore.LoadStoreParam private boolean disableChallengeResourceVerification = false; /** - * Stores the certificate refresh interval in milliseconds. + * Stores the certificate refresh interval. */ - private long certificatesRefreshIntervalInMs; + private Duration certificatesRefreshInterval = Duration.ZERO; /** * Stores a flag indicating whether AIA certificate downloads are disabled. @@ -108,13 +109,17 @@ public static KeyVaultLoadStoreParameter fromSystemProperties() { parameter.disableChallengeResourceVerification(); } + KeyVaultKeyStore.warnIfRemovedCertificatesRefreshIntervalIsConfigured(); String refreshInterval - = System.getProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS); - if (refreshInterval == null) { - refreshInterval = System.getProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL); - } + = System.getProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL); if (refreshInterval != null) { - parameter.setCertificatesRefreshIntervalInMs(Long.parseLong(refreshInterval)); + Duration interval; + try { + interval = Duration.ofMillis(Long.parseLong(refreshInterval)); + } catch (NumberFormatException exception) { + interval = Duration.parse(refreshInterval); + } + parameter.setCertificatesRefreshInterval(interval); } if (Boolean.parseBoolean(System.getProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_DISABLE_AIA_DOWNLOAD))) { @@ -300,22 +305,24 @@ public void disableChallengeResourceVerification() { } /** - * Gets the certificate refresh interval in milliseconds. + * Gets the certificate refresh interval. * - * @return The certificate refresh interval in milliseconds. + * @return The certificate refresh interval. Defaults to {@link Duration#ZERO}, which disables periodic refresh. */ - public long getCertificatesRefreshIntervalInMs() { - return certificatesRefreshIntervalInMs; + public Duration getCertificatesRefreshInterval() { + return certificatesRefreshInterval; } /** - * Sets the certificate refresh interval in milliseconds. + * Sets the certificate refresh interval. Zero or negative intervals disable periodic refresh. * - * @param certificatesRefreshIntervalInMs The certificate refresh interval in milliseconds. + * @param certificatesRefreshInterval The certificate refresh interval, for example {@code Duration.parse("PT30S")}. * @return The KeyVaultLoadStoreParameter. + * @throws NullPointerException if {@code certificatesRefreshInterval} is {@code null}. */ - public KeyVaultLoadStoreParameter setCertificatesRefreshIntervalInMs(long certificatesRefreshIntervalInMs) { - this.certificatesRefreshIntervalInMs = certificatesRefreshIntervalInMs; + public KeyVaultLoadStoreParameter setCertificatesRefreshInterval(Duration certificatesRefreshInterval) { + this.certificatesRefreshInterval + = Objects.requireNonNull(certificatesRefreshInterval, "'certificatesRefreshInterval' cannot be null."); return this; } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java index e23f157fbfbf6..1f53a3c277675 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java @@ -131,7 +131,7 @@ private void updateCertificateConfiguration(KeyVaultLoadStoreParameter parameter List updatedIncludeAliasPatterns = getAliasPatterns(normalizedFilterPatterns, false); List updatedExcludeAliasPatterns = getAliasPatterns(normalizedFilterPatterns, true); - certificatesRefreshIntervalInMs = parameter.getCertificatesRefreshIntervalInMs(); + certificatesRefreshIntervalInMs = parameter.getCertificatesRefreshInterval().toMillis(); includeAliasPatterns = updatedIncludeAliasPatterns; excludeAliasPatterns = updatedExcludeAliasPatterns; } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificatesTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificatesTest.java index d2d7fa7c9dcef..1a2fb8bd9feea 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificatesTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultCertificatesTest.java @@ -83,7 +83,7 @@ private X509Certificate getTestCertificate() { @Test public void testCertificatesRefreshInterval() throws Exception { - System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS, "1000"); + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT1S"); KeyStore keyStore = PropertyConvertorUtils.getKeyVaultKeyStore(); assertNotNull(keyStore.getCertificate(certificateName)); keyStore.deleteEntry(certificateName); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java index cd28a487bc82c..e0e1e56018c5d 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameterTest.java @@ -8,18 +8,33 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.api.parallel.Resources; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import java.text.MessageFormat; +import java.time.Duration; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ResourceLock(Resources.SYSTEM_PROPERTIES) public class KeyVaultLoadStoreParameterTest { + private static final String REMOVED_REFRESH_INTERVAL_PROPERTY + = "azure.keyvault.jca.certificates-refresh-interval-in-ms"; @BeforeEach @AfterEach @@ -34,7 +49,7 @@ public void clearSystemProperties() { System.clearProperty(KeyVaultJcaPropertyNames.CERT_PATH_WELL_KNOWN); System.clearProperty(KeyVaultJcaPropertyNames.CERT_PATH_CUSTOM); System.clearProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL); - System.clearProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS); + System.clearProperty(REMOVED_REFRESH_INTERVAL_PROPERTY); System.clearProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_REFRESH_CERTIFICATES_WHEN_HAVE_UNTRUST_CERTIFICATE); System.clearProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_DISABLE_AIA_DOWNLOAD); String aliasFilterProperty = KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATE_ALIAS_FILTER_PATTERN; @@ -56,7 +71,7 @@ public void testFromSystemPropertiesCapturesAllProperties() { System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_DISABLE_CHALLENGE_RESOURCE_VERIFICATION, "true"); System.setProperty(KeyVaultJcaPropertyNames.CERT_PATH_WELL_KNOWN, "/well-known"); System.setProperty(KeyVaultJcaPropertyNames.CERT_PATH_CUSTOM, "/custom"); - System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS, "1000"); + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT1S"); System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_REFRESH_CERTIFICATES_WHEN_HAVE_UNTRUST_CERTIFICATE, "true"); System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATE_ALIAS_FILTER_PATTERN, " alias-one "); @@ -75,7 +90,7 @@ public void testFromSystemPropertiesCapturesAllProperties() { assertTrue(parameter.isDisableChallengeResourceVerification()); assertEquals("/well-known", parameter.getCertPathWellKnown()); assertEquals("/custom", parameter.getCertPathCustom()); - assertEquals(1000L, parameter.getCertificatesRefreshIntervalInMs()); + assertEquals(Duration.ofSeconds(1), parameter.getCertificatesRefreshInterval()); assertTrue(parameter.isRefreshCertificatesWhenHaveUnTrustCertificate()); assertEquals(new HashSet<>(Arrays.asList("alias-one", "^prod-.*")), parameter.getCertificateAliasFilterPatterns()); @@ -89,26 +104,123 @@ public void testFromSystemPropertiesUsesDefaults() { assertNull(parameter.getUri()); assertEquals("/etc/certs/well-known/", parameter.getCertPathWellKnown()); assertEquals("/etc/certs/custom/", parameter.getCertPathCustom()); - assertEquals(0L, parameter.getCertificatesRefreshIntervalInMs()); + assertEquals(Duration.ZERO, parameter.getCertificatesRefreshInterval()); assertFalse(parameter.isRefreshCertificatesWhenHaveUnTrustCertificate()); assertTrue(parameter.getCertificateAliasFilterPatterns().isEmpty()); assertFalse(parameter.isDisableChallengeResourceVerification()); assertFalse(parameter.isAiaDownloadDisabled()); } + @ParameterizedTest + @ValueSource(strings = { "PT30S", "30000", "+30000" }) + public void testRefreshIntervalAcceptsIso8601DurationAndMilliseconds(String refreshInterval) { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, refreshInterval); + + assertEquals(Duration.ofSeconds(30), + KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval()); + } + @Test - public void testRefreshIntervalInMsTakesPrecedence() { + public void testLegacyRefreshIntervalMillisecondsAreSupported() { System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "2000"); - System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL_IN_MS, "1000"); - assertEquals(1000L, KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshIntervalInMs()); + assertEquals(Duration.ofMillis(2000), + KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval()); } @Test - public void testLegacyRefreshIntervalIsFallback() { - System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "2000"); + public void testRefreshIntervalAcceptsIso8601Duration() { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT30S"); + + assertEquals(Duration.ofSeconds(30), + KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval()); + } + + @Test + public void testRefreshIntervalAcceptsFractionalSeconds() { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT0.5S"); + + assertEquals(Duration.ofMillis(500), + KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval()); + } + + @ParameterizedTest + @ValueSource(strings = { "0", "PT0S", "-1", "-PT1S" }) + public void testRefreshIntervalAllowsDisabledRefresh(String refreshInterval) { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, refreshInterval); + + Duration interval = KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval(); + assertTrue(interval.isZero() || interval.isNegative()); + } + + @ParameterizedTest + @ValueSource(strings = { "", "not-a-duration", "PT", "30S" }) + public void testInvalidRefreshIntervalIsRejected(String refreshInterval) { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, refreshInterval); + + assertThrows(DateTimeParseException.class, KeyVaultLoadStoreParameter::fromSystemProperties); + } + + @Test + public void testProgrammaticRefreshInterval() { + KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter(null); + + assertSame(parameter, parameter.setCertificatesRefreshInterval(Duration.ofSeconds(30))); + assertEquals(Duration.ofSeconds(30), parameter.getCertificatesRefreshInterval()); + assertThrows(NullPointerException.class, () -> parameter.setCertificatesRefreshInterval(null)); + } + + @Test + public void testRemovedRefreshIntervalPropertyWarnsAndIsIgnored() { + assertRemovedRefreshIntervalPropertyWarnsAndIsIgnored(Duration.ZERO); + } + + @ParameterizedTest + @ValueSource(strings = { "PT30S", "30000" }) + public void testRemovedRefreshIntervalPropertyDoesNotOverrideSupportedProperty(String refreshInterval) { + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, refreshInterval); + + assertRemovedRefreshIntervalPropertyWarnsAndIsIgnored(Duration.ofSeconds(30)); + } + + private void assertRemovedRefreshIntervalPropertyWarnsAndIsIgnored(Duration expectedInterval) { + System.setProperty(REMOVED_REFRESH_INTERVAL_PROPERTY, "removed-property-value"); + Logger logger = Logger.getLogger(KeyVaultKeyStore.class.getName()); + Level originalLevel = logger.getLevel(); + List records = new ArrayList<>(); + Handler collector = new Handler() { + @Override + public void publish(LogRecord record) { + records.add(record); + } + + @Override + public void flush() { + } - assertEquals(2000L, KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshIntervalInMs()); + @Override + public void close() { + } + }; + logger.addHandler(collector); + logger.setLevel(Level.ALL); + try { + assertEquals(expectedInterval, + KeyVaultLoadStoreParameter.fromSystemProperties().getCertificatesRefreshInterval()); + assertEquals(1, records.size()); + LogRecord warning = records.get(0); + assertEquals(Level.WARNING, warning.getLevel()); + String message = MessageFormat.format(warning.getMessage(), warning.getParameters()); + assertTrue(message.contains(REMOVED_REFRESH_INTERVAL_PROPERTY)); + assertTrue(message.contains("removed and is ignored")); + assertTrue(message.contains(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL)); + assertTrue(message.contains("ISO-8601")); + assertTrue(message.contains("PT30S")); + assertFalse(message.contains("removed-property-value")); + } finally { + logger.removeHandler(collector); + logger.setLevel(originalLevel); + } } @Test @@ -129,13 +241,16 @@ public void testCertificateAliasFilterPatternsAreDefensivelyCopied() { public void testFromSystemPropertiesCreatesSnapshot() { System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_URI, "https://first.vault.azure.net"); System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_DISABLE_AIA_DOWNLOAD, "true"); + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT30S"); KeyVaultLoadStoreParameter parameter = KeyVaultLoadStoreParameter.fromSystemProperties(); System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_URI, "https://second.vault.azure.net"); System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_DISABLE_AIA_DOWNLOAD, "false"); + System.setProperty(KeyVaultJcaPropertyNames.KEYVAULT_JCA_CERTIFICATES_REFRESH_INTERVAL, "PT1S"); assertEquals("https://first.vault.azure.net", parameter.getUri()); assertTrue(parameter.isAiaDownloadDisabled()); + assertEquals(Duration.ofSeconds(30), parameter.getCertificatesRefreshInterval()); } } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificatesTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificatesTest.java index 0b6198b54b57d..2db0965ca5d62 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificatesTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificatesTest.java @@ -17,6 +17,7 @@ import java.lang.reflect.Field; import java.security.Key; import java.security.cert.Certificate; +import java.time.Duration; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -57,7 +58,7 @@ private KeyVaultCertificates createKeyVaultCertificates(KeyVaultClient client) { private KeyVaultCertificates createKeyVaultCertificates(KeyVaultClient client, Set filterPatterns) { KeyVaultLoadStoreParameter parameter - = new KeyVaultLoadStoreParameter(null).setCertificatesRefreshIntervalInMs(60_000) + = new KeyVaultLoadStoreParameter(null).setCertificatesRefreshInterval(Duration.ofSeconds(60)) .setCertificateAliasFilterPatterns(filterPatterns); return new KeyVaultCertificates(parameter, client); } @@ -228,7 +229,7 @@ public byte[] getEncoded() { }); keyVaultClient.stubCertificateForVersion(otherVersion, otherCertificate); KeyVaultLoadStoreParameter parameter - = new KeyVaultLoadStoreParameter(null).setCertificatesRefreshIntervalInMs(1); + = new KeyVaultLoadStoreParameter(null).setCertificatesRefreshInterval(Duration.ofMillis(1)); keyVaultCertificates = new KeyVaultCertificates(parameter, keyVaultClient); Assertions.assertEquals(matchingAlias, keyVaultCertificates.refreshAndGetAliasByCertificate(certificate)); diff --git a/sdk/keyvault/azure-security-test-keyvault-jca/pom.xml b/sdk/keyvault/azure-security-test-keyvault-jca/pom.xml index 87d87b7ef1a92..8856f926ddc39 100644 --- a/sdk/keyvault/azure-security-test-keyvault-jca/pom.xml +++ b/sdk/keyvault/azure-security-test-keyvault-jca/pom.xml @@ -25,7 +25,7 @@ com.azure azure-security-keyvault-jca - 2.13.0-beta.1 + 2.14.0-beta.1 diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml index 2c1dc467d8885..62a70d02eb596 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml @@ -283,7 +283,7 @@ com.azure azure-security-keyvault-jca - 2.12.0 + 2.13.0 true diff --git a/sdk/spring/spring-cloud-azure-starter-keyvault-jca/pom.xml b/sdk/spring/spring-cloud-azure-starter-keyvault-jca/pom.xml index fe0fb0cb78947..f0277e20afdcb 100644 --- a/sdk/spring/spring-cloud-azure-starter-keyvault-jca/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-keyvault-jca/pom.xml @@ -64,7 +64,7 @@ com.azure azure-security-keyvault-jca - 2.12.0 + 2.13.0 diff --git a/sdk/tools/azure-openrewrite/pom.xml b/sdk/tools/azure-openrewrite/pom.xml index e5945a1c52a3f..4bd5d08949711 100644 --- a/sdk/tools/azure-openrewrite/pom.xml +++ b/sdk/tools/azure-openrewrite/pom.xml @@ -273,7 +273,7 @@ com.azure azure-security-keyvault-jca - 2.12.0 + 2.13.0 test @@ -335,7 +335,7 @@ com.azure azure-security-keyvault-jca - 2.12.0 + 2.13.0 provided