Skip to content
Open
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
7 changes: 3 additions & 4 deletions sdk/keyvault/azure-security-keyvault-secrets/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Release History

## 4.11.0-beta.1 (Unreleased)
## 4.11.0-beta.1 (2026-03-18)

### Features Added

### Breaking Changes
- Added `getPreviousVersion()` method to `KeyVaultSecret` and related response models to return the previous version of a certificate, if applicable. Only applies to certificates created after June 1, 2025.
- Added `outContentType` query parameter to `getSecret` and `getSecretVersion` operations to support PFX to PEM conversion for certificate-backed secrets.

### Bugs Fixed

- Fixed an issue where certain `HttpResponseException.getResponse()` calls could cause a `NullPointerException`. ([#47801](https://github.com/Azure/azure-sdk-for-java/issues/47801))

### Other Changes

## 4.10.5 (2026-01-29)

### Other Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ private static void removeFiles(Editor editor) {
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/SecretAsyncClient.java");
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/SecretClient.java");
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/SecretClientBuilder.java");
// The generator now emits these names; they are replaced by our hand-crafted versions.
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/implementation/KeyVaultClientImpl.java");
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/KeyVaultClientBuilder.java");
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/KeyVaultClient.java");
editor.removeFile("src/main/java/com/azure/security/keyvault/secrets/KeyVaultAsyncClient.java");
}

private static void customizeServiceVersion(LibraryCustomization customization) {
Expand All @@ -49,7 +54,8 @@ private static void customizeServiceVersion(LibraryCustomization customization)
.addImplementedType("ServiceVersion")
.setJavadocComment("The versions of Azure Key Vault Secrets supported by this client library.");

for (String version : Arrays.asList("7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6")) {
for (String version : Arrays.asList("7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6",
"2025-06-01-preview", "2025-07-01")) {
enumDeclaration.addEnumConstant("V" + version.replace('.', '_').replace('-', '_').toUpperCase())
.setJavadocComment("Service version {@code " + version + "}.")
.addArgument(new StringLiteralExpr(version));
Expand All @@ -72,14 +78,21 @@ private static void customizeServiceVersion(LibraryCustomization customization)
.addBlockTag("return", "The latest {@link SecretServiceVersion}."))
.setBody(StaticJavaParser.parseBlock("{ return V7_6; }"));

customization.getRawEditor()
.addFile("src/main/java/com/azure/security/keyvault/secrets/SecretServiceVersion.java",
compilationUnit.toString());
Editor editor = customization.getRawEditor();
String svFileName = "src/main/java/com/azure/security/keyvault/secrets/SecretServiceVersion.java";
String svContent = compilationUnit.toString();
if (editor.getFileContent(svFileName) != null) {
editor.replaceFile(svFileName, svContent);
} else {
editor.addFile(svFileName, svContent);
}

String fileName = "src/main/java/com/azure/security/keyvault/secrets/implementation/SecretClientImpl.java";
String fileContent = customization.getRawEditor().getFileContent(fileName);
fileContent = fileContent.replace("KeyVaultServiceVersion", "SecretServiceVersion");
customization.getRawEditor().replaceFile(fileName, fileContent);
if (fileContent != null) {
fileContent = fileContent.replace("KeyVaultServiceVersion", "SecretServiceVersion");
customization.getRawEditor().replaceFile(fileName, fileContent);
}
}

private static void customizeModuleInfo(Editor editor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.azure.security.keyvault.secrets.implementation.models.SecretsModelsUtils;
import com.azure.security.keyvault.secrets.models.DeletedSecret;
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
import com.azure.security.keyvault.secrets.models.SecretContentType;
import com.azure.security.keyvault.secrets.models.SecretProperties;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -440,6 +441,94 @@ public Mono<Response<KeyVaultSecret>> getSecretWithResponse(String name, String
}
}

/**
* Gets the latest version of the specified certificate-backed secret from the key vault, requesting on-demand
* format conversion. Use this method to retrieve a certificate stored as a secret and convert it from PFX to
* PEM format in a single service call. This operation requires the {@code secrets/get} permission.
*
* <p>This method is only supported for service version {@code 2025-07-01} and later.
* Currently only PFX ({@link SecretContentType#PFX}) to PEM ({@link SecretContentType#PEM})
* conversion is supported.</p>
*
* <p><strong>Code sample</strong></p>
* <p>Gets a certificate-backed secret in PEM format.</p>
* <!-- src_embed com.azure.keyvault.secrets.SecretAsyncClient.getSecret#string-SecretContentType -->
* <pre>
* secretAsyncClient.getSecret&#40;&quot;myCertificateSecret&quot;, SecretContentType.PEM&#41;
* .subscribe&#40;pemSecret -&gt;
* System.out.printf&#40;&quot;Retrieved secret in PEM format, value starts with: %s%n&quot;,
* pemSecret.getValue&#40;&#41;.substring&#40;0, 27&#41;&#41;&#41;;
* </pre>
* <!-- end com.azure.keyvault.secrets.SecretAsyncClient.getSecret#string-SecretContentType -->
*
* @param name The name of the certificate-backed secret.
* @param outContentType The desired output format. Use {@link SecretContentType#PEM} to request
* PFX-to-PEM conversion.
* @return A {@link Mono} containing the requested {@link KeyVaultSecret} with its value in the
* specified format.
* @throws ResourceNotFoundException When a secret with the given {@code name} doesn't exist in the vault.
* @throws IllegalArgumentException If {@code name} is either {@code null} or empty.
* @throws HttpResponseException If {@code outContentType} specifies an unsupported format.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<KeyVaultSecret> getSecret(String name, SecretContentType outContentType) {
return getSecretWithResponse(name, "", outContentType).map(Response::getValue);
}

/**
* Gets the specified version of a certificate-backed secret from the key vault, requesting on-demand
* format conversion. Use this method to retrieve a certificate stored as a secret and convert it from PFX to
* PEM format in a single service call. This operation requires the {@code secrets/get} permission.
*
* <p>This method is only supported for service version {@code 2025-07-01} and later.
* Currently only PFX ({@link SecretContentType#PFX}) to PEM ({@link SecretContentType#PEM})
* conversion is supported.</p>
*
* <p><strong>Code sample</strong></p>
* <p>Gets a specific version of a certificate-backed secret in PEM format.</p>
* <!-- src_embed com.azure.keyvault.secrets.SecretAsyncClient.getSecretWithResponse#string-string-SecretContentType -->
* <pre>
* String secretVersion = &quot;6A385B124DEF4096AF1361A85B16C204&quot;;
* secretAsyncClient.getSecretWithResponse&#40;&quot;myCertificateSecret&quot;, secretVersion, SecretContentType.PEM&#41;
* .subscribe&#40;pemResponse -&gt;
* System.out.printf&#40;&quot;Retrieved secret in PEM format with status: %d%n&quot;,
* pemResponse.getStatusCode&#40;&#41;&#41;&#41;;
* </pre>
* <!-- end com.azure.keyvault.secrets.SecretAsyncClient.getSecretWithResponse#string-string-SecretContentType -->
*
* @param name The name of the certificate-backed secret, cannot be null.
* @param version The version of the secret to retrieve. If this is an empty string or null, the latest
* version is retrieved.
* @param outContentType The desired output format. Use {@link SecretContentType#PEM} to request
* PFX-to-PEM conversion.
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains the
* requested {@link KeyVaultSecret} with its value in the specified format.
* @throws ResourceNotFoundException When a secret with the given {@code name} and {@code version} doesn't
* exist in the vault.
* @throws IllegalArgumentException If {@code name} is either {@code null} or empty.
* @throws HttpResponseException If {@code outContentType} specifies an unsupported format.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<Response<KeyVaultSecret>> getSecretWithResponse(String name, String version,
SecretContentType outContentType) {
if (CoreUtils.isNullOrEmpty(name)) {
return monoError(LOGGER, new IllegalArgumentException("'name' cannot be null or empty."));
}

try {
RequestOptions requestOptions = new RequestOptions();
if (outContentType != null) {
requestOptions.addQueryParam("outContentType", outContentType.toString());
}
return implClient.getSecretWithResponseAsync(name, version != null ? version : "", requestOptions)
.onErrorMap(HttpResponseException.class, SecretAsyncClient::mapGetSecretException)
.map(response -> new SimpleResponse<>(response,
createKeyVaultSecret(response.getValue().toObject(SecretBundle.class))));
} catch (RuntimeException e) {
return monoError(LOGGER, e);
}
}

// For some reason, the service does not return a 409 when a secret with the same name exists. Instead, it returns
// a 403.
static HttpResponseException mapGetSecretException(HttpResponseException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.azure.security.keyvault.secrets.implementation.models.SecretUpdateParameters;
import com.azure.security.keyvault.secrets.models.DeletedSecret;
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
import com.azure.security.keyvault.secrets.models.SecretContentType;
import com.azure.security.keyvault.secrets.models.SecretProperties;

import java.time.Duration;
Expand Down Expand Up @@ -401,6 +402,70 @@ public Response<KeyVaultSecret> getSecretWithResponse(String name, String versio
}, SecretAsyncClient::mapGetSecretException);
}

/**
* Gets the latest version of the specified certificate-backed secret from the key vault, requesting
* on-demand format conversion. Use this method to retrieve a certificate stored as a secret and convert
* it from PFX to PEM format in a single service call.
* This operation requires the {@code secrets/get} permission.
*
* <p>Only available with service version {@link SecretServiceVersion#V2025_07_01} and later.
* Currently only PFX to PEM conversion is supported.</p>
*
* @param name The name of the certificate-backed secret.
* @param outContentType The desired output format. Use {@link SecretContentType#PEM} to request
* PFX-to-PEM conversion.
* @return The requested {@link KeyVaultSecret} with its value in the specified format.
* @throws ResourceNotFoundException When a secret with the given {@code name} does not exist in the vault.
* @throws IllegalArgumentException If {@code name} is either {@code null} or empty.
* @throws HttpResponseException If {@code outContentType} specifies an unsupported format.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public KeyVaultSecret getSecret(String name, SecretContentType outContentType) {
return getSecretWithResponse(name, "", outContentType, Context.NONE).getValue();
}

/**
* Gets the specified version of a certificate-backed secret from the key vault, requesting on-demand
* format conversion. Use this method to retrieve a certificate stored as a secret and convert it from
* PFX to PEM format in a single service call.
* This operation requires the {@code secrets/get} permission.
*
* <p>Only available with service version {@link SecretServiceVersion#V2025_07_01} and later.
* Currently only PFX to PEM conversion is supported.</p>
*
* @param name The name of the certificate-backed secret, cannot be null.
* @param version The version of the secret to retrieve. If this is an empty string or null, the latest
* version is retrieved.
* @param outContentType The desired output format. Use {@link SecretContentType#PEM} to request
* PFX-to-PEM conversion.
* @param context Additional context that is passed through the HTTP pipeline during the service call.
* @return A {@link Response} whose {@link Response#getValue() value} contains the requested
* {@link KeyVaultSecret} with its value in the specified format.
* @throws ResourceNotFoundException When a secret with the given {@code name} and {@code version} does
* not exist in the vault.
* @throws IllegalArgumentException If {@code name} is either {@code null} or empty.
* @throws HttpResponseException If {@code outContentType} specifies an unsupported format.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Response<KeyVaultSecret> getSecretWithResponse(String name, String version, SecretContentType outContentType,
Context context) {
if (CoreUtils.isNullOrEmpty(name)) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException("'name' cannot be null or empty."));
}

return callWithMappedException(() -> {
RequestOptions requestOptions = new RequestOptions().setContext(context);
if (outContentType != null) {
requestOptions.addQueryParam("outContentType", outContentType.toString());
}
Response<BinaryData> response
= implClient.getSecretWithResponse(name, version != null ? version : "", requestOptions);

return new SimpleResponse<>(response,
createKeyVaultSecret(response.getValue().toObject(SecretBundle.class)));
}, SecretAsyncClient::mapGetSecretException);
}

/**
* Updates the attributes associated with the secret. The value of the secret in the key vault cannot be changed.
* Only attributes populated in {@code secretProperties} are changed. Attributes not specified in the request are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,15 @@ public enum SecretServiceVersion implements ServiceVersion {
/**
* Service version {@code 7.6}.
*/
V7_6("7.6");
V7_6("7.6"),
/**
* Service version {@code 2025-06-01-preview}.
*/
V2025_06_01_PREVIEW("2025-06-01-preview"),
/**
* Service version {@code 2025-07-01}.
*/
V2025_07_01("2025-07-01");

private final String version;

Expand All @@ -59,6 +67,6 @@ public String getVersion() {
* @return The latest {@link SecretServiceVersion}.
*/
public static SecretServiceVersion getLatest() {
return V7_6;
return V2025_07_01;
}
}
Loading
Loading