diff --git a/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md index b38abbcb08340..e192e594eaf43 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md @@ -2,6 +2,9 @@ ## 4.4.0-beta.2 (Unreleased) +### Features Added +- Added `Exportable` and `ReleasePolicy` to `CreateKeyOptions`, `ImportKeyOptions`, and `KeyProperties` to support Secure Key Release for Key Vault and Managed HSM. +- Added a `release()` operation to `KeyClient` and `KeyAsyncClient` to release a key for Key Vault and Managed HSM. ## 4.4.0-beta.1 (2021-07-09) diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyAsyncClient.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyAsyncClient.java index eec511746dcc2..7923f5047e188 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyAsyncClient.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyAsyncClient.java @@ -16,6 +16,7 @@ import com.azure.core.http.rest.RestProxy; import com.azure.core.http.rest.SimpleResponse; import com.azure.core.util.Context; +import com.azure.core.util.CoreUtils; import com.azure.core.util.FluxUtil; import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.polling.LongRunningOperationStatus; @@ -36,6 +37,8 @@ import com.azure.security.keyvault.keys.models.KeyType; import com.azure.security.keyvault.keys.models.KeyVaultKey; import com.azure.security.keyvault.keys.models.RandomBytes; +import com.azure.security.keyvault.keys.models.ReleaseKeyOptions; +import com.azure.security.keyvault.keys.models.ReleaseKeyResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -228,7 +231,8 @@ Mono> createKeyWithResponse(CreateKeyOptions createKeyOpti .setKty(createKeyOptions.getKeyType()) .setKeyOps(createKeyOptions.getKeyOperations()) .setKeyAttributes(new KeyRequestAttributes(createKeyOptions)) - .setTags(createKeyOptions.getTags()); + .setTags(createKeyOptions.getTags()) + .setReleasePolicy(createKeyOptions.getReleasePolicy()); return service.createKey(vaultUrl, createKeyOptions.getName(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) @@ -311,7 +315,8 @@ Mono> createRsaKeyWithResponse(CreateRsaKeyOptions createR .setKeyOps(createRsaKeyOptions.getKeyOperations()) .setKeyAttributes(new KeyRequestAttributes(createRsaKeyOptions)) .setPublicExponent(createRsaKeyOptions.getPublicExponent()) - .setTags(createRsaKeyOptions.getTags()); + .setTags(createRsaKeyOptions.getTags()) + .setReleasePolicy(createRsaKeyOptions.getReleasePolicy()); return service.createKey(vaultUrl, createRsaKeyOptions.getName(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) @@ -399,7 +404,8 @@ Mono> createEcKeyWithResponse(CreateEcKeyOptions createEcK .setCurve(createEcKeyOptions.getCurveName()) .setKeyOps(createEcKeyOptions.getKeyOperations()) .setKeyAttributes(new KeyRequestAttributes(createEcKeyOptions)) - .setTags(createEcKeyOptions.getTags()); + .setTags(createEcKeyOptions.getTags()) + .setReleasePolicy(createEcKeyOptions.getReleasePolicy()); return service.createKey(vaultUrl, createEcKeyOptions.getName(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) @@ -484,7 +490,8 @@ Mono> createOctKeyWithResponse(CreateOctKeyOptions createO .setKty(createOctKeyOptions.getKeyType()) .setKeyOps(createOctKeyOptions.getKeyOperations()) .setKeyAttributes(new KeyRequestAttributes(createOctKeyOptions)) - .setTags(createOctKeyOptions.getTags()); + .setTags(createOctKeyOptions.getTags()) + .setReleasePolicy(createOctKeyOptions.getReleasePolicy()); return service.createKey(vaultUrl, createOctKeyOptions.getName(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) @@ -600,7 +607,8 @@ Mono> importKeyWithResponse(ImportKeyOptions importKeyOpti .setKey(importKeyOptions.getKey()) .setHsm(importKeyOptions.isHardwareProtected()) .setKeyAttributes(new KeyRequestAttributes(importKeyOptions)) - .setTags(importKeyOptions.getTags()); + .setTags(importKeyOptions.getTags()) + .setReleasePolicy(importKeyOptions.getReleasePolicy()); return service.importKey(vaultUrl, importKeyOptions.getName(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) @@ -768,10 +776,13 @@ Mono> updateKeyPropertiesWithResponse(KeyProperties keyPro context = context == null ? Context.NONE : context; KeyRequestParameters parameters = new KeyRequestParameters() .setTags(keyProperties.getTags()) - .setKeyAttributes(new KeyRequestAttributes(keyProperties)); + .setKeyAttributes(new KeyRequestAttributes(keyProperties)) + .setReleasePolicy(keyProperties.getReleasePolicy()); + if (keyOperations.length > 0) { parameters.setKeyOps(Arrays.asList(keyOperations)); } + return service.updateKey(vaultUrl, keyProperties.getName(), keyProperties.getVersion(), apiVersion, ACCEPT_LANGUAGE, parameters, CONTENT_TYPE_HEADER_VALUE, context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) .doOnRequest(ignored -> logger.verbose("Updating key - {}", keyProperties.getName())) @@ -1380,7 +1391,7 @@ private Mono> listKeyVersionsNextPage(String contin * * @param count The requested number of random bytes. * - * @return The requested number of bytes containing random values from a managed HSM. + * @return A {@link Mono} containing the requested number of bytes containing random values from a managed HSM. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono getRandomBytes(int count) { @@ -1403,7 +1414,8 @@ public Mono getRandomBytes(int count) { * * @param count The requested number of random bytes. * - * @return The requested number of bytes containing random values from a managed HSM. + * @return A {@link Mono} containing the {@link Response HTTP response} for this operation and the requested number + * of bytes containing random values from a managed HSM. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getRandomBytesWithResponse(int count) { @@ -1426,5 +1438,105 @@ Mono> getRandomBytesWithResponse(int count, Context contex return monoError(logger, e); } } + + /** + * Releases the latest version of a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param target The attestation assertion for the target of the key release. + * + * @return A {@link Mono} containing the {@link ReleaseKeyResult} containing the released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono releaseKey(String name, String target) { + try { + return releaseKeyWithResponse(name, "", target, new ReleaseKeyOptions()) + .flatMap(FluxUtil::toMono); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + + /** + * Releases a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param version The version of the key to retrieve. If this is empty or {@code null}, this call is equivalent to + * calling {@link KeyAsyncClient#releaseKey(String, String)}, with the latest key version being released. + * @param target The attestation assertion for the target of the key release. + * + * @return A {@link Mono} containing the {@link ReleaseKeyResult} containing the released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono releaseKey(String name, String version, String target) { + try { + return releaseKeyWithResponse(name, version, target, new ReleaseKeyOptions()) + .flatMap(FluxUtil::toMono); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + + /** + * Releases a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param version The version of the key to retrieve. If this is empty or {@code null}, this call is equivalent to + * calling {@link KeyAsyncClient#releaseKey(String, String)}, with the latest key version being released. + * @param target The attestation assertion for the target of the key release. + * @param options Additional options for releasing a key. + * + * @return A {@link Mono} containing the {@link Response HTTP response} for this operation and the + * {@link ReleaseKeyResult} containing the released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> releaseKeyWithResponse(String name, String version, String target, + ReleaseKeyOptions options) { + try { + return withContext(context -> releaseKeyWithResponse(name, version, target, options, context)); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } + + Mono> releaseKeyWithResponse(String name, String version, String target, + ReleaseKeyOptions options, Context context) { + try { + if (CoreUtils.isNullOrEmpty(name)) { + return monoError(logger, new IllegalArgumentException("'name' cannot be null or empty")); + } + + if (CoreUtils.isNullOrEmpty(target)) { + return monoError(logger, new IllegalArgumentException("'target' cannot be null or empty")); + } + + options = options == null ? new ReleaseKeyOptions() : options; + + KeyReleaseParameters keyReleaseParameters = new KeyReleaseParameters() + .setTarget(target) + .setAlgorithm(options.getAlgorithm()) + .setNonce(options.getNonce()); + + return service.release(vaultUrl, name, version, apiVersion, keyReleaseParameters, "application/json", + context.addData(AZ_TRACING_NAMESPACE_KEY, KEYVAULT_TRACING_NAMESPACE_VALUE)) + .doOnRequest(ignored -> logger.verbose("Releasing key with name %s and version %s.", name, version)) + .doOnSuccess(response -> logger.verbose("Released key with name %s and version %s.", name, version)) + .doOnError(error -> logger.warning("Failed to release key - {}", error)); + } catch (RuntimeException e) { + return monoError(logger, e); + } + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClient.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClient.java index bf6d92fa1ad88..8ee9de66e6146 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClient.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClient.java @@ -26,6 +26,8 @@ import com.azure.security.keyvault.keys.models.KeyOperation; import com.azure.security.keyvault.keys.models.KeyType; import com.azure.security.keyvault.keys.models.RandomBytes; +import com.azure.security.keyvault.keys.models.ReleaseKeyOptions; +import com.azure.security.keyvault.keys.models.ReleaseKeyResult; /** * The KeyClient provides synchronous methods to manage {@link KeyVaultKey keys} in the Azure Key Vault. The client supports @@ -915,9 +917,68 @@ public RandomBytes getRandomBytes(int count) { * @param count The requested number of random bytes. * @param context Additional context that is passed through the Http pipeline during the service call. * - * @return The requested number of bytes containing random values from a managed HSM. + * @return The {@link Response HTTP response} for this operation and the requested number of bytes containing + * random values from a managed HSM. */ public Response getRandomBytesWithResponse(int count, Context context) { return client.getRandomBytesWithResponse(count, context).block(); } + + /** + * Releases the latest version of a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param target The attestation assertion for the target of the key release. + * + * @return The key release result containing the released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ReleaseKeyResult releaseKey(String name, String target) { + return client.releaseKey(name, target).block(); + } + + /** + * Releases a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param version The version of the key to retrieve. If this is empty or {@code null}, this call is equivalent to + * calling {@link KeyAsyncClient#releaseKey(String, String)}, with the latest key version being released. + * @param target The attestation assertion for the target of the key release. + * + * @return The key release result containing the released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ReleaseKeyResult releaseKey(String name, String version, String target) { + return client.releaseKey(name, version, target).block(); + } + + /** + * Releases a key. + * + *

The key must be exportable. This operation requires the 'keys/release' permission.

+ * + * @param name The name of the key to release. + * @param version Version of the key to release.This parameter is optional. + * @param target The attestation assertion for the target of the key release. + * @param options Additional options for releasing a key. + * @param context Additional context that is passed through the Http pipeline during the service call. + * + * @return The {@link Response HTTP response} for this operation and the {@link ReleaseKeyResult} containing the + * released key. + * + * @throws IllegalArgumentException If {@code name} or {@code target} are {@code null} or empty. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response releaseKeyWithResponse(String name, String version, String target, + ReleaseKeyOptions options, Context context) { + return client.releaseKeyWithResponse(name, version, target, options, context).block(); + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyImportRequestParameters.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyImportRequestParameters.java index 1ca8cf1a3ef30..d57c71c98b01f 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyImportRequestParameters.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyImportRequestParameters.java @@ -5,6 +5,7 @@ import com.azure.core.annotation.Fluent; import com.azure.security.keyvault.keys.models.JsonWebKey; +import com.azure.security.keyvault.keys.models.KeyReleasePolicy; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; @@ -35,6 +36,12 @@ class KeyImportRequestParameters { @JsonProperty(value = "tags") private Map tags; + /* + * The policy rules under which the key can be exported. + */ + @JsonProperty(value = "release_policy") + private KeyReleasePolicy releasePolicy; + /** * Get the key attributes. * @@ -122,4 +129,26 @@ public KeyImportRequestParameters setKey(JsonWebKey key) { return this; } + + /** + * Get the policy rules under which the key can be exported. + * + * @return The policy rules under which the key can be exported. + */ + public KeyReleasePolicy getReleasePolicy() { + return this.releasePolicy; + } + + /** + * Set the policy rules under which the key can be exported. + * + * @param releasePolicy The policy rules under which the key can be exported. + * + * @return The updated {@link KeyImportRequestParameters} object. + */ + public KeyImportRequestParameters setReleasePolicy(KeyReleasePolicy releasePolicy) { + this.releasePolicy = releasePolicy; + + return this; + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyReleaseParameters.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyReleaseParameters.java new file mode 100644 index 0000000000000..b5131fe64adcb --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyReleaseParameters.java @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys; + +import com.azure.core.annotation.Fluent; +import com.azure.security.keyvault.keys.models.KeyExportEncryptionAlgorithm; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The release key parameters. + */ +@Fluent +class KeyReleaseParameters { + /* + * The attestation assertion for the target of the key release. + */ + @JsonProperty(value = "target", required = true) + private String target; + + /* + * A client provided nonce for freshness. + */ + @JsonProperty(value = "nonce") + private String nonce; + + /* + * The encryption algorithm to use to protected the exported key material + */ + @JsonProperty(value = "enc") + private KeyExportEncryptionAlgorithm encryptionAlgorithm; + + /** + * Get the attestation assertion for the target of the key release. + * + * @return The target value. + */ + public String getTarget() { + return this.target; + } + + /** + * Set the attestation assertion for the target of the key release. + * + * @param target The attestation assertion for the target of the key release. + * + * @return The updated {@link KeyReleaseParameters} object. + */ + public KeyReleaseParameters setTarget(String target) { + this.target = target; + + return this; + } + + /** + * Get a client provided nonce for freshness. + * + * @return A client provided nonce for freshness. + */ + public String getNonce() { + return this.nonce; + } + + /** + * Set a client provided nonce for freshness. + * + * @param nonce A client provided nonce for freshness. + * + * @return The updated {@link KeyReleaseParameters} object. + */ + public KeyReleaseParameters setNonce(String nonce) { + this.nonce = nonce; + + return this; + } + + /** + * Get the encryption algorithm to use to protected the exported key material. + * + * @return The encryption algorithm to use to protected the exported key material. + */ + public KeyExportEncryptionAlgorithm getAlgorithm() { + return this.encryptionAlgorithm; + } + + /** + * Set the encryption algorithm to use to protected the exported key material. + * + * @param encryptionAlgorithm The encryption algorithm to use to protected the exported key material. + * + * @return The updated {@link KeyReleaseParameters} object. + */ + public KeyReleaseParameters setAlgorithm(KeyExportEncryptionAlgorithm encryptionAlgorithm) { + this.encryptionAlgorithm = encryptionAlgorithm; + + return this; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestAttributes.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestAttributes.java index cf53e33fd2a9f..319f075ae617e 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestAttributes.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestAttributes.java @@ -28,6 +28,7 @@ class KeyRequestAttributes { } this.enabled = keyProperties.isEnabled(); + this.exportable = keyProperties.isExportable(); } /** @@ -47,6 +48,7 @@ class KeyRequestAttributes { } this.enabled = keyOptions.isEnabled(); + this.exportable = keyOptions.isExportable(); } /** @@ -79,6 +81,27 @@ class KeyRequestAttributes { @JsonProperty(value = "updated", access = JsonProperty.Access.WRITE_ONLY) private Long updated; + /** + * The number of days a key is retained before being deleted for a soft delete-enabled Key Vault. + */ + @JsonProperty(value = "recoverableDays", access = JsonProperty.Access.WRITE_ONLY) + private Integer recoverableDays; + + /** + * Reflects the deletion recovery level currently in effect for keys in the current vault. If it contains + * 'Purgeable', the key can be permanently deleted by a privileged user; otherwise, only the system can purge the + * key, at the end of the retention interval. Possible values include: 'Purgeable', 'Recoverable+Purgeable', + * 'Recoverable', 'Recoverable+ProtectedSubscription'. + */ + @JsonProperty(value = "recoveryLevel", access = JsonProperty.Access.WRITE_ONLY) + private String recoveryLevel; + + /* + * Indicates if the private key can be exported. + */ + @JsonProperty(value = "exportable") + private Boolean exportable; + /** * Get the enabled value. * @@ -186,4 +209,70 @@ public OffsetDateTime getUpdated() { return OffsetDateTime.ofInstant(Instant.ofEpochMilli(this.updated * 1000L), ZoneOffset.UTC); } + + /** + * Get a flag that indicates if the private key can be exported. + * + * @return A flag that indicates if the private key can be exported. + */ + public Boolean isExportable() { + return this.exportable; + } + + /** + * Set a flag that indicates if the private key can be exported. + * + * @param exportable A flag that indicates if the private key can be exported. + * + * @return The updated {@link KeyRequestAttributes} object. + */ + public KeyRequestAttributes setExportable(Boolean exportable) { + this.exportable = exportable; + + return this; + } + + /** + * Get the number of days a key is retained before being deleted for a soft delete-enabled Key Vault. + * + * @return The recoverable days. + */ + public Integer getRecoverableDays() { + return recoverableDays; + } + + /** + * Sets the number of days a key is retained before being deleted for a soft delete-enabled Key Vault. + * + * @param recoverableDays The recoverable days. + * + * @return The updated {@link KeyRequestAttributes} object. + */ + public KeyRequestAttributes setRecoverableDays(Integer recoverableDays) { + this.recoverableDays = recoverableDays; + + return this; + } + + /** + * Get the key recovery level. + * + * @return The key recovery level. + */ + public String getRecoveryLevel() { + return this.recoveryLevel; + } + + /** + * Get the key recovery level. + * + * @param recoveryLevel The key recovery level. + * + * @return The updated {@link KeyRequestAttributes} object. + */ + public KeyRequestAttributes setRecoveryLevel(String recoveryLevel) { + this.recoveryLevel = recoveryLevel; + + return this; + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestParameters.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestParameters.java index 9dd3ad7d4518d..2bd37c65bc6a2 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestParameters.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyRequestParameters.java @@ -6,6 +6,7 @@ import com.azure.core.annotation.Fluent; import com.azure.security.keyvault.keys.models.KeyCurveName; import com.azure.security.keyvault.keys.models.KeyOperation; +import com.azure.security.keyvault.keys.models.KeyReleasePolicy; import com.azure.security.keyvault.keys.models.KeyType; import com.fasterxml.jackson.annotation.JsonProperty; @@ -58,6 +59,12 @@ class KeyRequestParameters { @JsonProperty(value = "public_exponent") private int publicExponent; + /* + * The policy rules under which the key can be exported. + */ + @JsonProperty(value = "release_policy") + private KeyReleasePolicy releasePolicy; + /** * Get the key type. * @@ -210,4 +217,26 @@ public KeyRequestParameters setPublicExponent(int publicExponent) { this.publicExponent = publicExponent; return this; } + + /** + * Get the policy rules under which the key can be exported. + * + * @return The policy rules under which the key can be exported. + */ + public KeyReleasePolicy getReleasePolicy() { + return this.releasePolicy; + } + + /** + * Set the policy rules under which the key can be exported. + * + * @param releasePolicy The policy rules to set. + * + * @return The updated {@link KeyRequestParameters} object. + */ + public KeyRequestParameters setReleasePolicy(KeyReleasePolicy releasePolicy) { + this.releasePolicy = releasePolicy; + + return this; + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyService.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyService.java index ace8c1756ffff..0d835b249e6ed 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyService.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyService.java @@ -29,6 +29,7 @@ import com.azure.security.keyvault.keys.implementation.models.GetRandomBytesRequest; import com.azure.security.keyvault.keys.implementation.models.RandomBytes; import com.azure.security.keyvault.keys.models.DeletedKey; +import com.azure.security.keyvault.keys.models.ReleaseKeyResult; import com.azure.security.keyvault.keys.models.KeyVaultKey; import com.azure.security.keyvault.keys.models.KeyProperties; import reactor.core.publisher.Mono; @@ -73,12 +74,12 @@ Mono> getKey(@HostParam("url") String url, @UnexpectedResponseExceptionType(code = {403}, value = ResourceModifiedException.class) @UnexpectedResponseExceptionType(HttpResponseException.class) Mono> getKeyPoller(@HostParam("url") String url, - @PathParam("key-name") String keyName, - @PathParam("key-version") String keyVersion, - @QueryParam("api-version") String apiVersion, - @HeaderParam("accept-language") String acceptLanguage, - @HeaderParam("Content-Type") String type, - Context context); + @PathParam("key-name") String keyName, + @PathParam("key-version") String keyVersion, + @QueryParam("api-version") String apiVersion, + @HeaderParam("accept-language") String acceptLanguage, + @HeaderParam("Content-Type") String type, + Context context); @Put("keys/{key-name}") @ExpectedResponses({200}) @@ -132,11 +133,11 @@ Mono> getKeyVersions(@HostParam("url") String url, @UnexpectedResponseExceptionType(code = {404}, value = ResourceNotFoundException.class) @UnexpectedResponseExceptionType(HttpResponseException.class) Mono> backupKey(@HostParam("url") String url, - @PathParam("key-name") String keyName, - @QueryParam("api-version") String apiVersion, - @HeaderParam("accept-language") String acceptLanguage, - @HeaderParam("Content-Type") String type, - Context context); + @PathParam("key-name") String keyName, + @QueryParam("api-version") String apiVersion, + @HeaderParam("accept-language") String acceptLanguage, + @HeaderParam("Content-Type") String type, + Context context); @Post("keys/restore") @@ -211,11 +212,11 @@ Mono> getDeletedKey(@HostParam("url") String url, @ExpectedResponses({200, 404}) @UnexpectedResponseExceptionType(HttpResponseException.class) Mono> getDeletedKeyPoller(@HostParam("url") String url, - @PathParam("key-name") String keyName, - @QueryParam("api-version") String apiVersion, - @HeaderParam("accept-language") String acceptLanguage, - @HeaderParam("Content-Type") String type, - Context context); + @PathParam("key-name") String keyName, + @QueryParam("api-version") String apiVersion, + @HeaderParam("accept-language") String acceptLanguage, + @HeaderParam("Content-Type") String type, + Context context); @Delete("deletedkeys/{key-name}") @@ -223,11 +224,11 @@ Mono> getDeletedKeyPoller(@HostParam("url") String url, @UnexpectedResponseExceptionType(code = {404}, value = ResourceNotFoundException.class) @UnexpectedResponseExceptionType(HttpResponseException.class) Mono> purgeDeletedKey(@HostParam("url") String url, - @PathParam("key-name") String keyName, - @QueryParam("api-version") String apiVersion, - @HeaderParam("accept-language") String acceptLanguage, - @HeaderParam("Content-Type") String type, - Context context); + @PathParam("key-name") String keyName, + @QueryParam("api-version") String apiVersion, + @HeaderParam("accept-language") String acceptLanguage, + @HeaderParam("Content-Type") String type, + Context context); @Post("deletedkeys/{key-name}/recover") @@ -249,4 +250,15 @@ Mono> getRandomBytes(@HostParam("url") String url, @BodyParam("application/json") GetRandomBytesRequest parameters, @HeaderParam("Accept") String accept, Context context); + + @Post("keys/{key-name}/{key-version}/release") + @ExpectedResponses({200}) + @UnexpectedResponseExceptionType(HttpResponseException.class) + Mono> release(@HostParam("url") String url, + @PathParam("key-name") String keyName, + @PathParam("key-version") String keyVersion, + @QueryParam("api-version") String apiVersion, + @BodyParam("application/json") KeyReleaseParameters parameters, + @HeaderParam("Accept") String accept, + Context context); } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateEcKeyOptions.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateEcKeyOptions.java index fe74a43d7767d..f4d59e192fdf0 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateEcKeyOptions.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateEcKeyOptions.java @@ -54,6 +54,31 @@ public CreateEcKeyOptions setCurveName(KeyCurveName curveName) { return this; } + /** + * Get the HSM value of the key being created. + * + * @return The HSM value. + */ + public Boolean isHardwareProtected() { + return this.hardwareProtected; + } + + /** + * Set whether the key being created is of HSM type or not. + * + * @param hardwareProtected The HSM value to set. + * + * @return The {@link CreateEcKeyOptions} object. + */ + public CreateEcKeyOptions setHardwareProtected(Boolean hardwareProtected) { + this.hardwareProtected = hardwareProtected; + KeyType keyType = hardwareProtected ? KeyType.EC_HSM : KeyType.EC; + + setKeyType(keyType); + + return this; + } + /** * Set the key operations. * @@ -124,27 +149,28 @@ public CreateEcKeyOptions setEnabled(Boolean enabled) { } /** - * Set whether the key being created is of HSM type or not. + * Set a flag that indicates if the private key can be exported. * - * @param hardwareProtected The HSM value to set. + * @param exportable A flag that indicates if the private key can be exported. * - * @return The {@link CreateEcKeyOptions} object. + * @return The updated {@link CreateEcKeyOptions} object. */ - public CreateEcKeyOptions setHardwareProtected(Boolean hardwareProtected) { - this.hardwareProtected = hardwareProtected; - KeyType keyType = hardwareProtected ? KeyType.EC_HSM : KeyType.EC; - - setKeyType(keyType); + public CreateEcKeyOptions setExportable(Boolean exportable) { + super.setExportable(exportable); return this; } /** - * Get the HSM value of the key being created. + * Set the policy rules under which the key can be exported. * - * @return The HSM value. + * @param releasePolicy The policy rules to set. + * + * @return The updated {@link CreateEcKeyOptions} object. */ - public Boolean isHardwareProtected() { - return this.hardwareProtected; + public CreateEcKeyOptions setReleasePolicy(KeyReleasePolicy releasePolicy) { + super.setReleasePolicy(releasePolicy); + + return this; } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateKeyOptions.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateKeyOptions.java index 23d55c842bf7a..fb32722775d45 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateKeyOptions.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateKeyOptions.java @@ -50,6 +50,16 @@ public class CreateKeyOptions { */ private Boolean enabled; + /* + * Indicates if the private key can be exported. + */ + private Boolean exportable; + + /* + * The policy rules under which the key can be exported. + */ + private KeyReleasePolicy releasePolicy; + /** * Creates instance of {@link CreateKeyOptions} with {@code name} as key name and {@code keyType} as type of the * key. @@ -62,6 +72,28 @@ public CreateKeyOptions(String name, KeyType keyType) { this.keyType = keyType; } + /** + * Get the key name. + * + * @return The name of the key. + */ + public String getName() { + return this.name; + } + + /** + * Get the key type. + * + * @return The key type. + */ + public KeyType getKeyType() { + return this.keyType; + } + + void setKeyType(KeyType keyType) { + this.keyType = keyType; + } + /** * Get the key operations. * @@ -84,19 +116,6 @@ public CreateKeyOptions setKeyOperations(KeyOperation... keyOperations) { return this; } - /** - * Get the key type. - * - * @return The key type. - */ - public KeyType getKeyType() { - return this.keyType; - } - - void setKeyType(KeyType keyType) { - this.keyType = keyType; - } - /** * Get the {@link OffsetDateTime key's notBefore time} in UTC. * @@ -186,12 +205,46 @@ public CreateKeyOptions setEnabled(Boolean enabled) { } /** - * Get the key name. + * Get a flag that indicates if the private key can be exported. * - * @return The name of the key. + * @return A flag that indicates if the private key can be exported. */ - public String getName() { - return this.name; + public Boolean isExportable() { + return this.exportable; + } + + /** + * Set a flag that indicates if the private key can be exported. + * + * @param exportable A flag that indicates if the private key can be exported. + * + * @return The updated {@link CreateKeyOptions} object. + */ + public CreateKeyOptions setExportable(Boolean exportable) { + this.exportable = exportable; + + return this; + } + + /** + * Get the policy rules under which the key can be exported. + * + * @return The policy rules under which the key can be exported. + */ + public KeyReleasePolicy getReleasePolicy() { + return this.releasePolicy; } + /** + * Set the policy rules under which the key can be exported. + * + * @param releasePolicy The policy rules to set. + * + * @return The updated {@link CreateKeyOptions} object. + */ + public CreateKeyOptions setReleasePolicy(KeyReleasePolicy releasePolicy) { + this.releasePolicy = releasePolicy; + + return this; + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateOctKeyOptions.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateOctKeyOptions.java index 843146fabc91e..3817a6353aedf 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateOctKeyOptions.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateOctKeyOptions.java @@ -31,6 +31,53 @@ public CreateOctKeyOptions(String name) { super(name, KeyType.OCT); } + /** + * Set the key size in bits. + * + * @param keySize The key size in bits to set. + * + * @return The updated {@link CreateOctKeyOptions} object. + */ + public CreateOctKeyOptions setKeySize(Integer keySize) { + this.keySize = keySize; + + return this; + } + + /** + * Get the key size in bits. + * + * @return The key size in bits. + */ + public Integer getKeySize() { + return this.keySize; + } + + /** + * Set whether the key being created is of HSM type or not. + * + * @param hardwareProtected The HSM value to set. + * + * @return The updated {@link CreateOctKeyOptions} object. + */ + public CreateOctKeyOptions setHardwareProtected(Boolean hardwareProtected) { + this.hardwareProtected = hardwareProtected; + KeyType keyType = hardwareProtected ? KeyType.OCT_HSM : KeyType.OCT; + + setKeyType(keyType); + + return this; + } + + /** + * Get the HSM value of the key being created. + * + * @return the HSM value. + */ + public Boolean isHardwareProtected() { + return this.hardwareProtected; + } + /** * Set the key operations. * @@ -101,49 +148,28 @@ public CreateOctKeyOptions setEnabled(Boolean enabled) { } /** - * Set the key size in bits. + * Set a flag that indicates if the private key can be exported. * - * @param keySize The key size to set. + * @param exportable A flag that indicates if the private key can be exported. * * @return The updated {@link CreateOctKeyOptions} object. */ - public CreateOctKeyOptions setKeySize(Integer keySize) { - this.keySize = keySize; + public CreateOctKeyOptions setExportable(Boolean exportable) { + super.setExportable(exportable); return this; } /** - * Get the key size in bits. + * Set the policy rules under which the key can be exported. * - * @return The key size in bits. - */ - public Integer getKeySize() { - return this.keySize; - } - - /** - * Set whether the key being created is of HSM type or not. - * - * @param hardwareProtected The HSM value to set. + * @param releasePolicy The policy rules to set. * * @return The updated {@link CreateOctKeyOptions} object. */ - public CreateOctKeyOptions setHardwareProtected(Boolean hardwareProtected) { - this.hardwareProtected = hardwareProtected; - KeyType keyType = hardwareProtected ? KeyType.OCT_HSM : KeyType.OCT; - - setKeyType(keyType); + public CreateOctKeyOptions setReleasePolicy(KeyReleasePolicy releasePolicy) { + super.setReleasePolicy(releasePolicy); return this; } - - /** - * Get the HSM value of the key being created. - * - * @return the HSM value. - */ - public Boolean isHardwareProtected() { - return this.hardwareProtected; - } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateRsaKeyOptions.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateRsaKeyOptions.java index 72359fdce2a07..1ab9939f703d1 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateRsaKeyOptions.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/CreateRsaKeyOptions.java @@ -37,6 +37,75 @@ public CreateRsaKeyOptions(String name) { super(name, KeyType.RSA); } + /** + * Get the key size in bits. + * + * @return The key size in bits. + */ + public Integer getKeySize() { + return this.keySize; + } + + /** + * Set the key size in bits. + * + * @param keySize The key size in bits to set. + * + * @return The updated {@link CreateRsaKeyOptions} object. + */ + public CreateRsaKeyOptions setKeySize(Integer keySize) { + this.keySize = keySize; + + return this; + } + + /** + * Get the HSM value of the key being created. + * + * @return The HSM value. + */ + public Boolean isHardwareProtected() { + return this.hardwareProtected; + } + + /** + * Set whether the key being created is of HSM type or not. + * + * @param hardwareProtected The HSM value to set. + * + * @return The updated {@link CreateRsaKeyOptions} object. + */ + public CreateRsaKeyOptions setHardwareProtected(Boolean hardwareProtected) { + this.hardwareProtected = hardwareProtected; + KeyType keyType = hardwareProtected ? KeyType.RSA_HSM : KeyType.RSA; + + setKeyType(keyType); + + return this; + } + + /** + * Get the public exponent for the key. + * + * @return The public exponent. + */ + public Integer getPublicExponent() { + return publicExponent; + } + + /** + * Set the public exponent for the key. + * + * @param publicExponent The public exponent to set. + * + * @return The updated {@link CreateRsaKeyOptions} object. + */ + public CreateRsaKeyOptions setPublicExponent(Integer publicExponent) { + this.publicExponent = publicExponent; + + return this; + } + /** * Set the key operations. * @@ -107,70 +176,27 @@ public CreateRsaKeyOptions setEnabled(Boolean enabled) { } /** - * Set the key size. - * - * @param keySize The key size to set. - * - * @return The updated {@link CreateRsaKeyOptions} object. - */ - public CreateRsaKeyOptions setKeySize(Integer keySize) { - this.keySize = keySize; - - return this; - } - - /** - * Get the key size in bits. - * - * @return The key size in bits. - */ - public Integer getKeySize() { - return this.keySize; - } - - /** - * Set whether the key being created is of HSM type or not. + * Set a flag that indicates if the private key can be exported. * - * @param hardwareProtected The HSM value to set. + * @param exportable A flag that indicates if the private key can be exported. * * @return The updated {@link CreateRsaKeyOptions} object. */ - public CreateRsaKeyOptions setHardwareProtected(Boolean hardwareProtected) { - this.hardwareProtected = hardwareProtected; - KeyType keyType = hardwareProtected ? KeyType.RSA_HSM : KeyType.RSA; - - setKeyType(keyType); + public CreateRsaKeyOptions setExportable(Boolean exportable) { + super.setExportable(exportable); return this; } /** - * Get the HSM value of the key being created. + * Set the policy rules under which the key can be exported. * - * @return The HSM value. - */ - public Boolean isHardwareProtected() { - return this.hardwareProtected; - } - - /** - * Get the public exponent for the key. - * - * @return The public exponent. - */ - public Integer getPublicExponent() { - return publicExponent; - } - - /** - * Set the public exponent for the key. - * - * @param publicExponent The public exponent to set. + * @param releasePolicy The policy rules to set. * * @return The updated {@link CreateRsaKeyOptions} object. */ - public CreateRsaKeyOptions setPublicExponent(Integer publicExponent) { - this.publicExponent = publicExponent; + public CreateRsaKeyOptions setReleasePolicy(KeyReleasePolicy releasePolicy) { + super.setReleasePolicy(releasePolicy); return this; } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyExportEncryptionAlgorithm.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyExportEncryptionAlgorithm.java new file mode 100644 index 0000000000000..02580db71885d --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyExportEncryptionAlgorithm.java @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +import com.azure.core.util.ExpandableStringEnum; +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.Collection; + +/** Defines values for KeyExportEncryptionAlgorithm. */ +public final class KeyExportEncryptionAlgorithm extends ExpandableStringEnum { + /** Static value CKM_RSA_AES_KEY_WRAP for KeyExportEncryptionAlgorithm. */ + public static final KeyExportEncryptionAlgorithm CKM_RSA_AES_KEY_WRAP = fromString("CKM_RSA_AES_KEY_WRAP"); + + /** Static value RSA_AES_KEY_WRAP_256 for KeyExportEncryptionAlgorithm. */ + public static final KeyExportEncryptionAlgorithm RSA_AES_KEY_WRAP_256 = fromString("RSA_AES_KEY_WRAP_256"); + + /** Static value RSA_AES_KEY_WRAP_384 for KeyExportEncryptionAlgorithm. */ + public static final KeyExportEncryptionAlgorithm RSA_AES_KEY_WRAP_384 = fromString("RSA_AES_KEY_WRAP_384"); + + /** + * Creates or finds a {@link KeyExportEncryptionAlgorithm} from its string representation. + * + * @param name A name to look for. + * + * @return The corresponding {@link KeyExportEncryptionAlgorithm}. + */ + @JsonCreator + public static KeyExportEncryptionAlgorithm fromString(String name) { + return fromString(name, KeyExportEncryptionAlgorithm.class); + } + + /** + * Get a collection of all known {@link KeyExportEncryptionAlgorithm} values + * + * @return All known {@link KeyExportEncryptionAlgorithm} values. + */ + public static Collection values() { + return values(KeyExportEncryptionAlgorithm.class); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyProperties.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyProperties.java index 3bc4c69fe71a0..174dacb03646c 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyProperties.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyProperties.java @@ -32,6 +32,11 @@ public class KeyProperties { */ Boolean enabled; + /* + * Indicates if the private key can be exported. + */ + Boolean exportable; + /** * Not before date in UTC. */ @@ -95,6 +100,12 @@ public class KeyProperties { @JsonProperty(value = "recoverableDays", access = JsonProperty.Access.WRITE_ONLY) private Integer recoverableDays; + /* + * The policy rules under which the key can be exported. + */ + @JsonProperty(value = "release_policy") + private KeyReleasePolicy releasePolicy; + /** * Gets the number of days a key is retained before being deleted for a soft delete-enabled Key Vault. * @@ -104,6 +115,28 @@ public Integer getRecoverableDays() { return recoverableDays; } + /** + * Get the policy rules under which the key can be exported. + * + * @return The policy rules under which the key can be exported. + */ + public KeyReleasePolicy getReleasePolicy() { + return this.releasePolicy; + } + + /** + * Set the policy rules under which the key can be exported. + * + * @param releasePolicy The policy rules to set. + * + * @return The updated {@link KeyProperties} object. + */ + public KeyProperties setReleasePolicy(KeyReleasePolicy releasePolicy) { + this.releasePolicy = releasePolicy; + + return this; + } + /** * Get the key recovery level. * @@ -145,6 +178,28 @@ public KeyProperties setEnabled(Boolean enabled) { return this; } + /** + * Get a flag that indicates if the private key can be exported. + * + * @return A flag that indicates if the private key can be exported. + */ + public Boolean isExportable() { + return this.exportable; + } + + /** + * Set a flag that indicates if the private key can be exported. + * + * @param exportable A flag that indicates if the private key can be exported. + * + * @return The updated {@link KeyProperties} object. + */ + public KeyProperties setExportable(Boolean exportable) { + this.exportable = exportable; + + return this; + } + /** * Get the {@link OffsetDateTime key's notBefore time} in UTC. * @@ -268,6 +323,7 @@ public String getVersion() { @SuppressWarnings("unchecked") void unpackAttributes(Map attributes) { this.enabled = (Boolean) attributes.get("enabled"); + this.exportable = (Boolean) attributes.get("exportable"); this.notBefore = epochToOffsetDateTime(attributes.get("nbf")); this.expiresOn = epochToOffsetDateTime(attributes.get("exp")); this.createdOn = epochToOffsetDateTime(attributes.get("created")); diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyReleasePolicy.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyReleasePolicy.java new file mode 100644 index 0000000000000..48bbd15eb61b2 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyReleasePolicy.java @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +import com.azure.core.annotation.Fluent; +import com.azure.security.keyvault.keys.implementation.Base64UrlJsonDeserializer; +import com.azure.security.keyvault.keys.implementation.Base64UrlJsonSerializer; +import com.azure.security.keyvault.keys.implementation.ByteExtensions; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.util.Objects; + +/** + * A model that represents the policy rules under which the key can be exported. + */ +@Fluent +public final class KeyReleasePolicy { + /* + * Blob encoding the policy rules under which the key can be released. + */ + @JsonProperty(value = "data") + @JsonSerialize(using = Base64UrlJsonSerializer.class) + @JsonDeserialize(using = Base64UrlJsonDeserializer.class) + private byte[] data; + + /* + * Content type and version of key release policy. + */ + @JsonProperty(value = "contentType") + private String contentType; + + KeyReleasePolicy() { + // Empty constructor for Jackson Deserialization + } + + /** + * Creates an instance of {@link KeyReleasePolicy}. + * + * @param data A blob encoding the policy rules under which the key can be released. + */ + public KeyReleasePolicy(byte[] data) { + Objects.requireNonNull(data, "'data' cannot be null."); + + this.data = ByteExtensions.clone(data); + } + + /** + * Get a blob encoding the policy rules under which the key can be released. + * + * @return A blob encoding the policy rules under which the key can be released. + */ + public byte[] getData() { + return ByteExtensions.clone(this.data); + } + + /** + * Get the content type and version of key release policy. + * + * @return The content type and version of key release policy. + */ + public String getContentType() { + return this.contentType; + } + + /** + * Set the content type and version of key release policy. + * + *

The service default is "application/json; charset=utf-8".

+ * + * @param contentType The content type and version of key release policy to set. + * + * @return The updated {@link KeyReleasePolicy} object. + */ + public KeyReleasePolicy setContentType(String contentType) { + this.contentType = contentType; + + return this; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKey.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKey.java index 7421caab5a47d..600e6b17259da 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKey.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKey.java @@ -125,4 +125,9 @@ private void setTags(Map tags) { private void setManaged(boolean managed) { properties.setManaged(managed); } + + @JsonProperty("release_policy") + private void setReleasePolicy(KeyReleasePolicy releasePolicy) { + properties.setReleasePolicy(releasePolicy); + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyOptions.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyOptions.java new file mode 100644 index 0000000000000..e3adc86fff57e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyOptions.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +/** + * Represents the configurable options to release a key. + */ +public class ReleaseKeyOptions { + /* + * A client provided nonce for freshness. + */ + private String nonce; + + /* + * The encryption algorithm to use to protected the exported key material + */ + private KeyExportEncryptionAlgorithm encryptionAlgorithm; + + /** + * Get a client provided nonce for freshness. + * + * @return A client provided nonce for freshness. + */ + public String getNonce() { + return this.nonce; + } + + /** + * Set a client provided nonce for freshness. + * + * @param nonce A client provided nonce for freshness. + * + * @return The updated {@link ReleaseKeyOptions} object. + */ + public ReleaseKeyOptions setNonce(String nonce) { + this.nonce = nonce; + + return this; + } + + /** + * Get the encryption algorithm to use to protected the exported key material. + * + * @return The encryption algorithm to use to protected the exported key material. + */ + public KeyExportEncryptionAlgorithm getAlgorithm() { + return this.encryptionAlgorithm; + } + + /** + * Set the encryption algorithm to use to protected the exported key material. + * + * @param encryptionAlgorithm The encryption algorithm to use to protected the exported key material. + * + * @return The updated {@link ReleaseKeyOptions} object. + */ + public ReleaseKeyOptions setAlgorithm(KeyExportEncryptionAlgorithm encryptionAlgorithm) { + this.encryptionAlgorithm = encryptionAlgorithm; + + return this; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyResult.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyResult.java new file mode 100644 index 0000000000000..1414b4dc7eac2 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/ReleaseKeyResult.java @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +import com.azure.core.annotation.Immutable; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** The release result, containing the released key. */ +@Immutable +public final class ReleaseKeyResult { + /* + * A signed object containing the released key. + */ + @JsonProperty(value = "value", access = JsonProperty.Access.WRITE_ONLY) + private String value; + + /** + * Get a signed object containing the released key. + * + * @return A signed object containing the released key. + */ + public String getValue() { + return this.value; + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyAsyncClientTest.java b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyAsyncClientTest.java index e100764af3bf0..dd8c2afee0d9b 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyAsyncClientTest.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyAsyncClientTest.java @@ -8,6 +8,7 @@ import com.azure.core.exception.ResourceNotFoundException; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpPipeline; +import com.azure.core.test.TestMode; import com.azure.core.util.polling.AsyncPollResponse; import com.azure.core.util.polling.LongRunningOperationStatus; import com.azure.core.util.polling.PollerFlux; @@ -16,11 +17,13 @@ import com.azure.security.keyvault.keys.models.KeyProperties; import com.azure.security.keyvault.keys.models.KeyType; import com.azure.security.keyvault.keys.models.KeyVaultKey; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import reactor.test.StepVerifier; +import java.io.IOException; import java.net.HttpURLConnection; import java.nio.ByteBuffer; import java.time.Duration; @@ -31,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -509,6 +513,41 @@ public void listKeys(HttpClient httpClient, KeyServiceVersion serviceVersion) { }); } + /** + * Tests that an existing key can be released. + */ + @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + @MethodSource("getTestParameters") + public void releaseKey(HttpClient httpClient, KeyServiceVersion serviceVersion) { + // TODO: Remove assumption once Key Vault allows for creating exportable keys. + Assumptions.assumeTrue(isManagedHsmTest); + + createKeyAsyncClient(httpClient, serviceVersion); + releaseKeyRunner((keyToRelease, attestationUrl) -> { + StepVerifier.create(client.createRsaKey(keyToRelease)) + .assertNext(keyResponse -> assertKeyEquals(keyToRelease, keyResponse)).verifyComplete(); + + String target = "testAttestationToken"; + + if (getTestMode() != TestMode.PLAYBACK) { + if (!attestationUrl.endsWith("/")) { + attestationUrl = attestationUrl + "/"; + } + + try { + target = getAttestationToken(attestationUrl + "generate-test-token"); + } catch (IOException e) { + fail("Found error when deserializing attestation token.", e); + } + } + + StepVerifier.create(client.releaseKey(keyToRelease.getName(), target)) + .assertNext(releaseKeyResult -> assertNotNull(releaseKeyResult.getValue())) + .expectComplete() + .verify(); + }); + } + /** * Tests that an RSA key with a public exponent can be created in the key vault. */ diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTest.java b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTest.java index 3e13ceb46b56c..b021603515c37 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTest.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTest.java @@ -8,6 +8,9 @@ import com.azure.core.exception.ResourceNotFoundException; import com.azure.core.http.HttpClient; import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLogOptions; +import com.azure.core.test.TestMode; import com.azure.core.util.polling.PollResponse; import com.azure.core.util.polling.SyncPoller; import com.azure.security.keyvault.keys.models.CreateKeyOptions; @@ -15,9 +18,12 @@ import com.azure.security.keyvault.keys.models.KeyProperties; import com.azure.security.keyvault.keys.models.KeyType; import com.azure.security.keyvault.keys.models.KeyVaultKey; +import com.azure.security.keyvault.keys.models.ReleaseKeyResult; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.io.IOException; import java.net.HttpURLConnection; import java.time.Duration; import java.util.ArrayList; @@ -28,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -45,6 +52,7 @@ protected void createKeyClient(HttpClient httpClient, KeyServiceVersion serviceV .vaultUrl(getEndpoint()) .pipeline(httpPipeline) .serviceVersion(serviceVersion) + .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) .buildAsyncClient()); if (interceptorManager.isPlaybackMode()) { @@ -389,7 +397,6 @@ public void getDeletedKey(HttpClient httpClient, KeyServiceVersion serviceVersio }); } - /** * Tests that deleted keys can be listed in the key vault. */ @@ -453,6 +460,39 @@ public void listKeyVersions(HttpClient httpClient, KeyServiceVersion serviceVers }); } + /** + * Tests that an existing key can be released. + */ + @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + @MethodSource("getTestParameters") + public void releaseKey(HttpClient httpClient, KeyServiceVersion serviceVersion) { + // TODO: Remove assumption once Key Vault allows for creating exportable keys. + Assumptions.assumeTrue(isManagedHsmTest); + + createKeyClient(httpClient, serviceVersion); + releaseKeyRunner((keyToRelease, attestationUrl) -> { + assertKeyEquals(keyToRelease, client.createRsaKey(keyToRelease)); + + String target = "testAttestationToken"; + + if (getTestMode() != TestMode.PLAYBACK) { + if (!attestationUrl.endsWith("/")) { + attestationUrl = attestationUrl + "/"; + } + + try { + target = getAttestationToken(attestationUrl + "generate-test-token"); + } catch (IOException e) { + fail("Found error when deserializing attestation token.", e); + } + } + + ReleaseKeyResult releaseKeyResult = client.releaseKey(keyToRelease.getName(), target); + + assertNotNull(releaseKeyResult.getValue()); + }); + } + private DeletedKey pollOnKeyPurge(String keyName) { int pendingPollCount = 0; while (pendingPollCount < 10) { diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTestBase.java b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTestBase.java index 3b248751948e2..d2d30a0bb17f0 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTestBase.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/KeyClientTestBase.java @@ -6,8 +6,11 @@ import com.azure.core.credential.TokenCredential; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpMethod; import com.azure.core.http.HttpPipeline; import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.ExponentialBackoff; import com.azure.core.http.policy.HttpLogDetailLevel; @@ -23,14 +26,13 @@ import com.azure.core.test.TestMode; import com.azure.core.util.Configuration; import com.azure.core.util.CoreUtils; +import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.core.util.serializer.SerializerEncoding; import com.azure.identity.ClientSecretCredentialBuilder; -import com.azure.security.keyvault.keys.models.CreateKeyOptions; -import com.azure.security.keyvault.keys.models.CreateOctKeyOptions; -import com.azure.security.keyvault.keys.models.CreateRsaKeyOptions; -import com.azure.security.keyvault.keys.models.KeyType; -import com.azure.security.keyvault.keys.models.KeyVaultKey; +import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -41,6 +43,13 @@ import java.util.UUID; import java.util.stream.Stream; +import com.azure.security.keyvault.keys.models.CreateKeyOptions; +import com.azure.security.keyvault.keys.models.CreateOctKeyOptions; +import com.azure.security.keyvault.keys.models.CreateRsaKeyOptions; +import com.azure.security.keyvault.keys.models.KeyReleasePolicy; +import com.azure.security.keyvault.keys.models.KeyType; +import com.azure.security.keyvault.keys.models.KeyVaultKey; +import com.fasterxml.jackson.annotation.JsonProperty; import org.junit.jupiter.api.Test; import java.time.OffsetDateTime; @@ -397,6 +406,39 @@ void getRandomBytesRunner(Consumer testRunner) { testRunner.accept(count); } + @Test + public abstract void releaseKey(HttpClient httpClient, KeyServiceVersion keyServiceVersion); + + void releaseKeyRunner(BiConsumer testRunner) { + final String attestationUrl = Configuration.getGlobalConfiguration() + .get("AZURE_KEYVAULT_ATTESTATION_URL", "http://localhost:8080"); + final String releasePolicyContents = + "{" + + "\"anyOf\": [" + + "{" + + "\"anyOf\": [" + + "{" + + "\"claim\": \"sdk-test\"," + + "\"condition\": \"equals\"," + + "\"value\": \"true\"" + + "}" + + "]," + + "\"authority\": \"" + attestationUrl + "\"" + + "}" + + "]," + + "\"version\": \"1.0\"" + + "}"; + + final CreateRsaKeyOptions keyToRelease = + new CreateRsaKeyOptions(testResourceNamer.randomName("keyToRelease", 20)) + .setKeySize(2048) + .setHardwareProtected(isManagedHsmTest) + .setReleasePolicy(new KeyReleasePolicy(releasePolicyContents.getBytes(StandardCharsets.UTF_8))) + .setExportable(true); + + testRunner.accept(keyToRelease, attestationUrl); + } + String generateResourceId(String suffix) { if (interceptorManager.isPlaybackMode()) { return suffix; @@ -579,4 +621,33 @@ protected static BigInteger toBigInteger(byte[] b) { return new BigInteger(b); } + + public static class AttestationToken { + @JsonProperty + String token; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + } + + public static String getAttestationToken(String attestationUrl) throws IOException { + HttpClient attestationClient = HttpClient.createDefault(); + + try (HttpResponse httpResponse = attestationClient + .send(new HttpRequest(HttpMethod.GET, attestationUrl)).block()) { + + assertNotNull(httpResponse); + + JacksonAdapter jacksonAdapter = new JacksonAdapter(); + AttestationToken attestationToken = + jacksonAdapter.deserialize(httpResponse.getBodyAsByteArray().block(), + AttestationToken.class, SerializerEncoding.JSON); + return attestationToken.getToken(); + } + } } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyAsyncClientTest.releaseKey[1].json b/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyAsyncClientTest.releaseKey[1].json new file mode 100644 index 0000000000000..ea9eaa44b545a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyAsyncClientTest.releaseKey[1].json @@ -0,0 +1,52 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.managedhsm.azure.net/keys/keytorelease06795b/create?api-version=7.3-preview", + "Headers" : { + "User-Agent" : "azsdk-java-client_name/client_version (11.0.6; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "content-length" : "972", + "x-frame-options" : "SAMEORIGIN", + "retry-after" : "0", + "StatusCode" : "200", + "strict-transport-security" : "max-age=31536000; includeSubDomains", + "x-ms-server-latency" : "579", + "content-security-policy" : "default-src 'self'", + "x-content-type-options" : "nosniff", + "content-type" : "application/json; charset=utf-8", + "x-ms-keyvault-region" : "centralus", + "x-ms-keyvault-network-info" : "conn_type=Ipv4;addr=189.181.107.174;act_addr_fam=Ipv4;", + "cache-control" : "no-cache", + "x-ms-request-id" : "2580265e-f719-11eb-b2a7-00224843a6b5", + "Body" : "{\"attributes\":{\"created\":1628297294,\"enabled\":true,\"exportable\":true,\"recoverableDays\":90,\"recoveryLevel\":\"Recoverable+Purgeable\",\"updated\":1628297294},\"key\":{\"e\":\"AQAB\",\"key_ops\":[\"wrapKey\",\"decrypt\",\"encrypt\",\"unwrapKey\",\"sign\",\"verify\"],\"kid\":\"https://vicolinamhsmhsm.managedhsm.azure.net/keys/keytorelease06795b/44257620186a0c6d13b6423a56f794f4\",\"kty\":\"RSA-HSM\",\"n\":\"wbxoZL_9ebOXQhLi8F3RcwhvkesbGx4is4E6i2IFJN4FOSbKqDB7bYBBx-azNNm2OxV42YdTVdM9HaUluULQBeBXkxYPpJchRQB7CYbcBVaCYDHIFeLiAvtU5jhFwgbDRDBZz7pcDs29KSglQGScDlkWbSbv4ZhPx8SRG4VSM2Y1QAd-V82Gg94P1RyTdxjtVAXEeFxGcfyj9W-YU1aeLPCUBZvXM6euleEAi0fK9rDAsaFdnkDeFCmQayyeqg4RGOfNprjT440Y_dRGhrZCpskwkGOm500cejCCBK9L8WKP4wjEP2wIVnXnIP51_ZqU3laVcORXAlGpkE5XITqBlw\"},\"release_policy\":{\"contentType\":\"application/json; charset=utf-8\",\"data\":\"eyJhbnlPZiI6W3siYW55T2YiOlt7ImNsYWltIjoic2RrLXRlc3QiLCJlcXVhbHMiOiJ0cnVlIn1dLCJhdXRob3JpdHkiOiJodHRwczovL3NrcmF0dGVzdGF0aW9uLmF6dXJld2Vic2l0ZXMubmV0LyJ9XSwidmVyc2lvbiI6IjEuMC4wIn0\"}}" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.managedhsm.azure.net/keys/keytorelease06795b//release?api-version=7.3-preview", + "Headers" : { + "User-Agent" : "azsdk-java-client_name/client_version (11.0.6; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "content-length" : "14286", + "x-frame-options" : "SAMEORIGIN", + "retry-after" : "0", + "StatusCode" : "200", + "strict-transport-security" : "max-age=31536000; includeSubDomains", + "x-ms-server-latency" : "425", + "content-security-policy" : "default-src 'self'", + "x-content-type-options" : "nosniff", + "content-type" : "application/json; charset=utf-8", + "x-ms-keyvault-region" : "centralus", + "x-ms-keyvault-network-info" : "conn_type=Ipv4;addr=189.181.107.174;act_addr_fam=Ipv4;", + "cache-control" : "no-cache", + "x-ms-request-id" : "2674dc30-f719-11eb-b2a7-00224843a6b5", + "Body" : "{\"value\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijd5eFFPdjhsTjREajQtWVZtVWtkdTR2X0FPMVV0d2hnTG1WYVBFVUhiWVkiLCJ4NWMiOlsiTUlJSkVUQ0NCdm1nQXdJQkFnSVRNd0FYZXgxUGZvTDlpMDhoL1FBQUFCZDdIVEFOQmdrcWhraUc5dzBCQVF3RkFEQlpNUXN3Q1FZRFZRUUdFd0pWVXpFZU1Cd0dBMVVFQ2hNVlRXbGpjbTl6YjJaMElFTnZjbkJ2Y21GMGFXOXVNU293S0FZRFZRUURFeUZOYVdOeWIzTnZablFnUVhwMWNtVWdWRXhUSUVsemMzVnBibWNnUTBFZ01EWXdIaGNOTWpFd09EQTJNak15TVRNeldoY05Nakl3T0RBeE1qTXlNVE16V2pCOU1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQk1DVjBFeEVEQU9CZ05WQkFjVEIxSmxaRzF2Ym1ReEhqQWNCZ05WQkFvVEZVMXBZM0p2YzI5bWRDQkRiM0p3YjNKaGRHbHZiakV2TUMwR0ExVUVBd3dtS2k1MmFXTnZiR2x1WVcxb2MyMW9jMjB1YldGdVlXZGxaR2h6YlM1aGVuVnlaUzV1WlhRd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUNNMkVqNXMrUkh5bU9PTUl1NHB6OFA0VHVFaUpQOW5NUnk0ZTB3NmJnY0ZXL2hFV0grZVZGMmRXNFRKV2hzSGErcU9FVFNva3VEcXdueEhxZ09YUTd0VkFydFFsdVhteXZuYnVra3BGZUlaSUNqNEFrS2lHQUM4VWRiZnhmdVBTRDB6OGJOS25ERUZpWWVCc1pNaC9SQ3M2WnlIUm9BZTdXdDl6d1NlVlpUTEJaaWMralFuM2crbDRVaXZyQS92YzhEY3VMbC9aU05YSXA4TXhWU05nSEVkZnlsS2RySXVNd2gzajlZeEdybW1jQjBtdWlTK1VHb2tqTnljUEZnaGpmcTBsSFZUd0Z2SEwvMVhDTXVMWmtvVFBScnRKVDIzRWtZOWwvdE8zbk04VUZaT0N6SGFoOSs5LzZCK2dWT1F6T3hUR3FMaVZaVkpUTS9PSkYrQTd3WkFnTUJBQUdqZ2dTc01JSUVxRENDQWZZR0Npc0dBUVFCMW5rQ0JBSUVnZ0htQklJQjRnSGdBSFlBS1htKzhKNDVPU0h3Vm5PZlk2VjM1YjVYZlp4Z0N2ajVUVjBtWENWZHg0UUFBQUY3SGM3QmVnQUFCQU1BUnpCRkFpRUFnY2REaDl4QlRycE4zYTUwNlIrKzNYV0FuN29kcFFZMXQrK3U5bG1FUGhBQ0lGejZhUlhXWVRWVXFSalByQ2U2UDBPOWdlYXRBQUtIc0VNSS82SUhnRVRWQUhZQTdrdTl0M1hPWUxyaFFta2ZxK0dlWnFNUGZsK3djdGlEQU1SN2lYcW8vY3NBQUFGN0hjN0JTZ0FBQkFNQVJ6QkZBaUVBbHVpdittZmZCZ2E1TjUyMllxUjlMb3p5N1hadWs4dVpaU3pPUUhDQUR3Y0NJQWVJOFlCZG4raUIyMTdtQU5TT3BDZVZTY2pUOEtqNmo2UndYQmd1T2hOWUFIWUFRY2pLc2Q4aVJrb1F4cUU2Q1VLSFhrNHhpeHNENit0THgyandrR0tXQnZZQUFBRjdIYzdCclFBQUJBTUFSekJGQWlFQTNFcWVPQ20rWnZrLzlYS2ZqUGxzS2MxWElyRHJCOUdKVWxTNkVrRzlrNGdDSURhQ1ZkeXdsUFhvWkxVc29rSTFJbjQwV2RiTFhWR1Z5S1dkR1NyMXdvTGJBSFlBVWFPdzlmMEJlWnhXYmJnM2VJOE1wSHJNR3lmTDk1NklRcG9OL3RTTEJlVUFBQUY3SGM3QnhBQUFCQU1BUnpCRkFpQlJORHdOdXQ1WHFOd2ZRTFk0TXhZK2Y3SWk4N0VTVnE3VzQwZWkvejB6YXdJaEFQa3E2UGg2Y085Vmg4YzNrZEYzUTVuMWlQYVdTNGdqWm9JSmwzaHVFeXFVTUNjR0NTc0dBUVFCZ2pjVkNnUWFNQmd3Q2dZSUt3WUJCUVVIQXdJd0NnWUlLd1lCQlFVSEF3RXdQQVlKS3dZQkJBR0NOeFVIQkM4d0xRWWxLd1lCQkFHQ054VUloNzNYRzRIbjYwYUNnWjB1anRBTWgvRGFIVjJDaE9WcGd2T25QZ0lCWkFJQkl6Q0JyZ1lJS3dZQkJRVUhBUUVFZ2FFd2daNHdiUVlJS3dZQkJRVUhNQUtHWVdoMGRIQTZMeTkzZDNjdWJXbGpjbTl6YjJaMExtTnZiUzl3YTJsdmNITXZZMlZ5ZEhNdlRXbGpjbTl6YjJaMEpUSXdRWHAxY21VbE1qQlVURk1sTWpCSmMzTjFhVzVuSlRJd1EwRWxNakF3TmlVeU1DMGxNakI0YzJsbmJpNWpjblF3TFFZSUt3WUJCUVVITUFHR0lXaDBkSEE2THk5dmJtVnZZM053TG0xcFkzSnZjMjltZEM1amIyMHZiMk56Y0RBZEJnTlZIUTRFRmdRVUU4eFpVNzBXMm45aDhFMThJeVp2Nk84eU9nb3dEZ1lEVlIwUEFRSC9CQVFEQWdTd01GY0dBMVVkRVFSUU1FNkNKaW91ZG1samIyeHBibUZ0YUhOdGFITnRMbTFoYm1GblpXUm9jMjB1WVhwMWNtVXVibVYwZ2lSMmFXTnZiR2x1WVcxb2MyMW9jMjB1YldGdVlXZGxaR2h6YlM1aGVuVnlaUzV1WlhRd1pBWURWUjBmQkYwd1d6QlpvRmVnVllaVGFIUjBjRG92TDNkM2R5NXRhV055YjNOdlpuUXVZMjl0TDNCcmFXOXdjeTlqY213dlRXbGpjbTl6YjJaMEpUSXdRWHAxY21VbE1qQlVURk1sTWpCSmMzTjFhVzVuSlRJd1EwRWxNakF3Tmk1amNtd3daZ1lEVlIwZ0JGOHdYVEJSQmd3ckJnRUVBWUkzVElOOUFRRXdRVEEvQmdnckJnRUZCUWNDQVJZemFIUjBjRG92TDNkM2R5NXRhV055YjNOdlpuUXVZMjl0TDNCcmFXOXdjeTlFYjJOekwxSmxjRzl6YVhSdmNua3VhSFJ0TUFnR0JtZUJEQUVDQWpBZkJnTlZIU01FR0RBV2dCVFZ3V2M2d3FPZDlIZFNXMWtTT0NubVZXaTdwVEFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQWdZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQkFCYUtWcUxoL29hbyt4NTJoSWttODlyOWJHa2IyOXZ6ZzQ3M01JMGRDSTJvV1BFelhyQXlZRFh5UWhBcHVPdEE5S1o2aWhoRm5ldDA1OGpJR2FJUXFNOXNDdngzWHNFR1BYaG5CUVppc1lrU0ZyandMMEV6a0JSKzUxaVlCbGZYU1M1MnhhMWp3T0hTcHFCZ1FCTkhKbTJEVkNTMWhZZ2ZtVXJPMFpVSjVBN2MrOVhPVkNpNHdNSFpLbkw1R1Y1QnkrM3FZd1lkU0xHa1IzZ2RmTVJ3ai80NEJ1VUxMNmlQWUFUTGJydWM5UWh5K1RKTFFlNXBYRVhRcy80cUpiM2EvREhDTUNmSjBzZmdhdERkWTJUYWZZZnE1SnZFUkEyZ0d6RGNNUHBRWS8rWUN2YjVuWGZTTGZIWG8vb0hGQ2c0VEJDWDJCZDJKV2UvODllallIdDZmUXNmbE9uMVgxZk1nQ0xQbWk1UUxYL3ZFeDBzVks1cXdDNk90Q0ZoN3doNEJ2Z1EyS09BelhhZXF4WGxuNjZrRk5uemV4OENqeXRWZnB1UnBMN2pqUEJGN3ZUbis0L1JlbUI4SE4yR1FlRDFWUGpuV2JYUkRwdm9GMTliVFZleUIyZkRRQ3JaVCtVMSsvYkNVNnJ0NllBT2t5YnR3NTUzdDJXTHRsM0ZTZzFna2lYRFhKUmplTHBaYTlQZW5ZWXY3aWhVM3FabWluYkp6WW1iS2o4b2hLS2pyOHlUNnV1UTlSK1ltTzI1RE1mNFkrQWgwTXQxNUZhMjljNmlDSmlhZUdGUXRDaUo5Q3JBbVdBdmtnRzZsbTNNZ2xxcmZPd3FraHVzTVBzSWdkTmtFRGNCaEdGQkVWSjR2U0dyN2UzZVBBeUdETS9mcEhuQ1lKU3Ryc3BGelVCbSIsIk1JSUY4ekNDQk51Z0F3SUJBZ0lRQXVlUmNmdUFJZWsvNHRtRGcweFF3REFOQmdrcWhraUc5dzBCQVF3RkFEQmhNUXN3Q1FZRFZRUUdFd0pWVXpFVk1CTUdBMVVFQ2hNTVJHbG5hVU5sY25RZ1NXNWpNUmt3RndZRFZRUUxFeEIzZDNjdVpHbG5hV05sY25RdVkyOXRNU0F3SGdZRFZRUURFeGRFYVdkcFEyVnlkQ0JIYkc5aVlXd2dVbTl2ZENCSE1qQWVGdzB5TURBM01qa3hNak13TURCYUZ3MHlOREEyTWpjeU16VTVOVGxhTUZreEN6QUpCZ05WQkFZVEFsVlRNUjR3SEFZRFZRUUtFeFZOYVdOeWIzTnZablFnUTI5eWNHOXlZWFJwYjI0eEtqQW9CZ05WQkFNVElVMXBZM0p2YzI5bWRDQkJlblZ5WlNCVVRGTWdTWE56ZFdsdVp5QkRRU0F3TmpDQ0FpSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBTFZHQVJsNTZieDNLQlVTR3VQYzRINXVvTkZrRkg0ZTdwdlRDeFJpNGovK3orWGJ3akV6KzVDaXBET3FqeDkvaldqc2tMNWRrN1BhUWt6SXRpZHNBQW5EQ1cxbGVaQk9JaTY4TGZmMWJqVGVaZ01ZaXdkUmQzWTM5Yi9sY0dwaXVQMmQyM1c5NVlIa01NVDhJbFdvc1lJWDBmNGtZYjYycnBoeWZuQWpZYi80T2Q5OVRobmhsQXhHdGZ2U2JYY0JWSUtDWWZaZ3FSdlYrNWxSZVVuZDFhTmpSWVZ6UE9vaWZnU3gyZlJ5eTErcE8xVXphTU1ObklPRTcxYlZZVzBBMWhyMTl3N2tPYjBLa0pYb0FMVEREajF1a1VFRHFRdUJmQnhSZUw1bVhpdTFPN1dHMHZsdGcwVlovU1p6Y3RCc2RCbHgxQmttV1lCVzI2MUtaZ0JpdnJxbDVFTFRLS2Q4cWd0SGNMUUE1Zmw2SkIwUWdzNVhEYVdlaE44NkdwczVKVzhBcmpHdGpjV0FJUCtYOENRYVdmYUNudVJtNkJrLzAzUFFXaGdkaTg0cXdBMHNzUmZGSndIVVBUTlNuRThFaUdWazJmcnQwdThQRzFwd1NRc0Z1TkpmY1lJSEV2MXZPelA3dUVPdUR5ZHNtQ2pobHh1b0sybjUvMmFWUjNCTVR1K3A0K2dsOGFsWG9CeWN5TG1qM0ovUFVncUQ4U0w1ZlRDVWVnR3NkaWEvU2E2ME4yb1Y3dlExN3dqTU4rTFhhMnJqai9iNFpsWmdYVm9qRG1BakR3SVJkRFV1alF1MFJWc0pxRkxNelNJSHBwMkNacDdtSW9McnlTYXkyWVlCdTdTaU53TDk1WDZIZTJrUzhlZWZCQkhqendXLzlGeEdxcnk1N2k3MWMyY0RBZ01CQUFHamdnR3RNSUlCcVRBZEJnTlZIUTRFRmdRVTFjRm5Pc0tqbmZSM1VsdFpFamdwNWxWb3U2VXdId1lEVlIwakJCZ3dGb0FVVGlKVUlCaVY1dU51NWcvNitya1M3UVlYanprd0RnWURWUjBQQVFIL0JBUURBZ0dHTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVNCZ05WSFJNQkFmOEVDREFHQVFIL0FnRUFNSFlHQ0NzR0FRVUZCd0VCQkdvd2FEQWtCZ2dyQmdFRkJRY3dBWVlZYUhSMGNEb3ZMMjlqYzNBdVpHbG5hV05sY25RdVkyOXRNRUFHQ0NzR0FRVUZCekFDaGpSb2RIUndPaTh2WTJGalpYSjBjeTVrYVdkcFkyVnlkQzVqYjIwdlJHbG5hVU5sY25SSGJHOWlZV3hTYjI5MFJ6SXVZM0owTUhzR0ExVWRId1IwTUhJd042QTFvRE9HTVdoMGRIQTZMeTlqY213ekxtUnBaMmxqWlhKMExtTnZiUzlFYVdkcFEyVnlkRWRzYjJKaGJGSnZiM1JITWk1amNtd3dONkExb0RPR01XaDBkSEE2THk5amNtdzBMbVJwWjJsalpYSjBMbU52YlM5RWFXZHBRMlZ5ZEVkc2IySmhiRkp2YjNSSE1pNWpjbXd3SFFZRFZSMGdCQll3RkRBSUJnWm5nUXdCQWdFd0NBWUdaNEVNQVFJQ01CQUdDU3NHQVFRQmdqY1ZBUVFEQWdFQU1BMEdDU3FHU0liM0RRRUJEQVVBQTRJQkFRQjJvV2M5M2ZCOGVzY2kvOGVzaXhqKytOMjJtZWlHRGpnRityQTJMVUs1SU9RT2djVVNUR0tTcUY5bFlmQXhQanJxUGpEQ1VQSENVUnYrMjZhZDVQL0JZdFh0Ym10eEpXdStjUzVCaE1EUFBlRzNvUFp3WFJIQkpGQWtZNE80QUY3UklBQVVXNkV6RGZsVW9ESEt2ODN6T2lQZllHY3BIYzlza3hBSW5DZWRrN1FTZ1h2TUFSampPcWRha29yMjFEVG1OSVVvdHhvOGtIdjVod1JsR2hCSndwczZmRVZpMUJ0MHRycE0vM3dZeGxyNDczV1NQVUZaUGdQMWo1MTlrTHBXT0o4ejA5d3hheStCcjI5aXJQY0JZdjBHTVhsSHFUaHk4eTRtL0h5VFFlSTJJTXZNclFud3FQcFkrckxJWHl2aUkydkxvSSs0eEtFNFJuMzhaWjhtIiwiTUlJRGpqQ0NBbmFnQXdJQkFnSVFBenJ4NXFjUnFhQzdLR1N4SFFuNjVUQU5CZ2txaGtpRzl3MEJBUXNGQURCaE1Rc3dDUVlEVlFRR0V3SlZVekVWTUJNR0ExVUVDaE1NUkdsbmFVTmxjblFnU1c1ak1Sa3dGd1lEVlFRTEV4QjNkM2N1WkdsbmFXTmxjblF1WTI5dE1TQXdIZ1lEVlFRREV4ZEVhV2RwUTJWeWRDQkhiRzlpWVd3Z1VtOXZkQ0JITWpBZUZ3MHhNekE0TURFeE1qQXdNREJhRncwek9EQXhNVFV4TWpBd01EQmFNR0V4Q3pBSkJnTlZCQVlUQWxWVE1SVXdFd1lEVlFRS0V3eEVhV2RwUTJWeWRDQkpibU14R1RBWEJnTlZCQXNURUhkM2R5NWthV2RwWTJWeWRDNWpiMjB4SURBZUJnTlZCQU1URjBScFoybERaWEowSUVkc2IySmhiQ0JTYjI5MElFY3lNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6Zk5OTng3YThteWFKQ3RTblgvUnJvaENnaU45UmxVeWZ1STIvT3U4anFKa1R4NjVxc0dHbXZQckMzb1hna2tSTHBpbW43V282aCs0RlIxSUFXc1VMZWNZeHBzTU56YUh4bXgxeDdlL2RmZ3k1U0RONjdzSDBOTzNYc3MwcjB1cFMva3FiaXRPdFNacExZbDZadHJBR0NTWVA5UElVa1k5MmVRcTJFR25JL3l1dW0wNlpJeWE3WHpWK2hkRzgyTUhhdVZCSlZKOHpVdGx1TkpiZDEzNC90SlM3U3NWUWVwajVXenRDTzdURzFGOFBhcHNwVXd0UDFNVll3blNsY1VmSUtkelhPUzB4WktCZ3lNVU5HUEhnbStGNkhtSWNyOWcrVVF2SU9sQ3NSbktQWnpGQlE5Um5iRGh4U0pJVFJOcnc5RkRLWkpvYnE3bk1XeE00TXBoUUlEQVFBQm8wSXdRREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTRHQTFVZER3RUIvd1FFQXdJQmhqQWRCZ05WSFE0RUZnUVVUaUpVSUJpVjV1TnU1Zy82K3JrUzdRWVhqemt3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUdCbktKUnZEa2hqNnpIZDZtY1kxWWw5UE1XTFNuL3B2dHNyRjkrd1gzTjNLaklUT1lGblFvUWo4a1ZuTmV5SXYvaVBzR0VNTktTdUlFeUV4dHY0TmVGMjJkK21RcnZIUkFpR2Z6WjBKRnJhYkEwVVdUVzk4a25kdGgvSnN3MUhLajJaTDd0Y3U3WFVJT0daWDFOR0ZkdG9tL0R6TU5VK01lS05oSjdqaXRyYWxqNDFFNlZmOFBsd1VIQkhRUkZYR1U3QWo2NEd4SlVURnk4YkpaOTE4ckdPbWFGdkU3RkJjZjZJS3NoUEVDQlYxL01VUmVYZ1JQVHFoNVV5a3c3K1UwYjZMSjMvaXlLNVM5a0pSYVRlcExpYVdOMGJmVktmamxsRGlJR2tuaWJWYjYzZERjWTNmZTBEa2h2bGQxOTI3anlOeEYxV1c2TFpabTZ6TlRmbE1yWT0iXSwieDV0I1MyNTYiOiI3eXhRT3Y4bE40RGo0LVlWbVVrZHU0dl9BTzFVdHdoZ0xtVmFQRVVIYllZIn0.eyJyZXF1ZXN0Ijp7ImFwaS12ZXJzaW9uIjoiNy4zLXByZXZpZXciLCJlbmMiOiJDS01fUlNBX0FFU19LRVlfV1JBUCIsImtpZCI6Imh0dHBzOi8vdmljb2xpbmFtaHNtaHNtLm1hbmFnZWRoc20uYXp1cmUubmV0L2tleXMva2V5dG9yZWxlYXNlMDY3OTViIn0sInJlc3BvbnNlIjp7ImtleSI6eyJhdHRyaWJ1dGVzIjp7ImNyZWF0ZWQiOjE2MjgyOTcyOTQsImVuYWJsZWQiOnRydWUsImV4cG9ydGFibGUiOnRydWUsInJlY292ZXJhYmxlRGF5cyI6OTAsInJlY292ZXJ5TGV2ZWwiOiJSZWNvdmVyYWJsZStQdXJnZWFibGUiLCJ1cGRhdGVkIjoxNjI4Mjk3Mjk0fSwia2V5Ijp7ImUiOiJBUUFCIiwia2V5X2hzbSI6ImV5SmphWEJvWlhKMFpYaDBJam9pYTJoVlNHaFhaMHBtTFVGTGNsSjVlR2x1UkdSS2NVcDBkMGRHTUZoVWVXUkVTVmRJYXpkQ1JHaGlTbEZYTnpOS016Sm1hekJTY0VORWRFSlBMWFJ6VjFKdWNTMXFhblJ1T0MxcWFIZEdZMDB4V1hwQkxTMVJVVTl1YzFaV1FWUXpka05LVTFGeE0zZzJiMDVuYkZNM09ITjFVR1JwZDNsV2VISk5RMlZaTTNWSU4zUnpUR1pIVkdGU1ppMW5XbkZ0VjNNNVNIUkNaM2hDVG10NVRqVjVUa1p3UzJkTk1EaHBTSEpCV0ZNMlJ6TlNUSEZyYVdsNloxY3llbGxMY2w5eVN6aFljR1pMUzFFNWFGOWFaVEk0T1hwNmJXcFBNVmwzY1Y5ZlMzVmpha05PZGtKaFNqQTFTelJNUkVGMU5GRm1RVmRtUlRSUlpYazJSbTl1Y25VeFFqRTBjM1JQZEdOWU4wRlRaVTVoWWpFeGRsTnZSMDF1UWpjeVFuUndXRFJyUzA1WmVHcENTMXBEV1RseWFtNUhWM2RRTXpnMGVEZEtjbnBMTmxjMFRVNVhTMEkyYldoME1HSTNNVkJ6VEZreVdXdDViREZzZWtvMFUyeFJOakYxTUZoc2FERk5lbmc1T0ZaM1VFaExkR1ExUXpsVGMyTkVSMEkzV0VvM1QzVnVOM2hIYldNNWFHTkVXVlpFZFRORFVFWmxRVjlvYTFacE5tdHliMjFRTTFwVmFuVlBibEpDZVhGVlUzUkZTRkpxUzNka1oyZHVaSGRZZGxwRWJ6VlFTM0kzY3prNVRXRmFja1pQVkRZM2NUZFFSMUZEVGtwelltNWFSMnRMTlhCRGNFaHlNSGhZUjBRdFRqVlVSa013ZEVaUGFVZGFSR3RSYTJWSVdGQnhSV1Z1VGw5M09GOXJaUzA1WVZGcGRrZE1YMEpvVUV4aFpGZHhTa1puYkVsRlgyOU5URmx1TVd0MFJVZHpPRFl0VjNrMGRHTlVSbEZhVkdwR2VIZEhTM0V5U1VjeFVISkliVXQwYVVFMVMzaGtlbmh0UjE4elJtSndRVVl0WDFFMU1USkRZbnBOYzFwVldGOUxaMnM0ZUVsRWMybGxNM05sVlhSVGNUYzFXRFkwWDBOblVUQlZXWEkyVWtOMVpVMTZiMDB5VldrMlF6UmtlVEp5TjFGS1dUWlVPRmhmVTFkRVdtdGhURzVqT0ZKbmMwNHdaM0pZVjFjMlgxSjNSMnhQVFZKeU1sRmtUV0ZWV0ZGQ1kybFpiVmhrTW1oTlZHOW9WbXBhTUZObGFsbERVbFV4U0dOV1VHUlhWR0pJYWxJMVpsa3pNbmhSVWtndGQxUlNOVnBqV1RKMU9WUmpMWFpDZGkxeVR5MVZYekpOVjI5alQzVXhjR1ZmU1ZwS1UwNTBUVTFXYUROQ1FWaEpjMUZPT1U1Rk9HSlNWQzFPYzI5d1VVWjRaR2xrTUc5RGRsRmxhMU5zYW14d1RFOHlWM0pWT1Rka01FTnJMUzFaTkRWamVGQm1RVUZXZGxWS1ltWkhjMUJRVURSc1NGaDNhalZXY1RKeWNUQlVVMnRxYjFsQlVFa3pZa2RVZGxSZk4xaG1kelZqZFVkc1NIcFZiazFmUmxkM1MzTkVWR3MyT0ZodGMwaHVhVVZDUVZwTFlXZEtkMHhuTFd3MVVHVldiVXBmUkZCblZHZFBlamhCTUY5NFFtNHpSVlZJTnpCMVNYcDNWRXBDV0dsVmJUUlRlVzlWZFRWNU1YWXdaR2g0Y0ZScE5taFVXbEpIYlhGSmFrTlBjVkZRZGt3MWRWY3lSVWt0TVY5RlNEWlBhbVZWZDI5dVFYZEZUblk0Ym5sbk56TmhRM1ZWV0RkSGFUWjJWbU5UVkhsMVNHRllRa0pFUjNSb2NVbHBNbDh5YlZwQmJsbGxkVzkxVWxKRlZuaHZRbTB0VkdkVVpWbGhYemhUZVVaUE9GVXhVazk1YzJ0elJqSjFjRTA1V2xOR2VVMW9OV3BVWDI5dGVqUTJNVE0zVFdGR1ZUSXdOemgzYldSSVRWbFBVWGg0T0c0eVIyNTRkWE5TVkhBeWVubDRWMUp5VG1JMFEyVmFhMjh3YlhKMGFIa3dlbGR4UkVsck5rUlJObUY2Vkd4NVRqYzBNRzFPZGtGVldqZElhMHRrV1RGMExWWkVjRXhLYlRnM1FVZzRhM0JEVTFwaFptOVFZMHd4UVcxM1lXdExTMFJGWlZsRlZXOWlhM2RQTUVaa2VuaHNNM2xzV2tscVFrZzJhWGgwV1hsVWQyeDRRMlZDUjNkelZYVXpiVk0yT1Zkc2JrWlVjRkJQWlV4d2RsWTVlV3RNUldSeE1ESnBXRzluUmsxTmVqZEJUVkZYVGs5cE1XbFlXalYzVjBSTFVISlVWRVZ5WlZOaVEzaG1NSEZKV2psRFRFaGhXRVJ0UzJaeGNGQldaMXB1WTBaUFNsRndiR3QzU1c4MFpGTnZOM1prUWtaaWNVaDNhMGd6Ykc1c1owVkxVSG81U0VGVVlUaFFjRTVJYlhkWlYzaDJiRE5pYjNKMk0wWmpiVTE2Y1dOaVFVRmxTVkZOTXpWNloxTjFTMDlaTm1GRVZuUkViMDVVYlc5Wk5rRlplVWhQZUhKUE1EVnBhWEJuZURsWmJtZzJTVXR1ZEVGWVptSjBaR0pvY0RoNGFsaHdRMk5KY2tvMU0waHFVamQyWDFNeFIzaG1RV1pEY2psak9IRkVRMUUzUVRCSFdWRTFNemcwUWtOS2RtWnZibU5QUldsRVJXbDZVa1ZJV1haVmJXVjNlbloyVDE5MmFHODFhV2RhYkZkck1GVXhibEZrVmxRMVNuRjBhRnAwYTFOa1MyNURUelJPVFZGUmRHbGtlVk16Vm14RFRISnlZM3BqVDE4M2VrcEJjbU4xY0MxM1IxVm5UR3hQU20wdE1XRnRaR3BwTFU5cFJIRkJUMTl6TVZoUlFsTnplWHBsVDFwRVpFZG5aa3hrVFRsR2VrWmhVemd6TW5sWGFISjRRbVExTUdGeVVqWmhaVWRaVnpWUk1XdENSVU5LY0V4aGMwcEtVVFZUY0d0alNtUk9XRmRMUWxSRVdYSk5kVEJPUjFOSFdqSndiMlpZUkVkZk5UZEhOR1ZQTURCZlZYUTJOSFpNTFhkV1NGazRiVzFoUkdkSVUzZEVNelZKV0VGWmMwbHNWa3RWY2pSTlNYSTNjMnA0VmtaaFkwZElObEUxVjJkUWVEUmliVlpDYUVKVFQzRnNTbFEyU25CV016bFlaVzlDY0dGSVIxaG5VRTVDTlZrNVJVdEhRUzE2YzNJNE9VUldXRk14ZW5aRFpVdEJVRzV2ZUdRME1YVkJNWEpSWlRCeldFWlFhVmx2YTI1elRGcGZabEF0YkV0c2RtVk9abEF4TjFCQ1IxUmxPRUpXU0hsRVVWWkdWVTFFYjNaTGRVczNPVEpXUVY5amRHMUllVVpCUjJSaWNEWnNhVUZJUWtJMFpWRjFPVnA2WTNOV1RDSXNJbWhsWVdSbGNpSTZleUpoYkdjaU9pSmthWElpTENKbGJtTWlPaUpEUzAxZlVsTkJYMEZGVTE5TFJWbGZWMUpCVUNJc0ltdHBaQ0k2SW1aaGEyVXRjbVZzWldGelpTMXJaWGtpZlN3aWMyTm9aVzFoWDNabGNuTnBiMjRpT2lJeExqQWlmUSIsImtleV9vcHMiOlsid3JhcEtleSIsImRlY3J5cHQiLCJlbmNyeXB0IiwidW53cmFwS2V5Iiwic2lnbiIsInZlcmlmeSJdLCJraWQiOiJodHRwczovL3ZpY29saW5hbWhzbWhzbS5tYW5hZ2VkaHNtLmF6dXJlLm5ldC9rZXlzL2tleXRvcmVsZWFzZTA2Nzk1Yi80NDI1NzYyMDE4NmEwYzZkMTNiNjQyM2E1NmY3OTRmNCIsImt0eSI6IlJTQSIsIm4iOiJ3YnhvWkxfOWViT1hRaExpOEYzUmN3aHZrZXNiR3g0aXM0RTZpMklGSk40Rk9TYktxREI3YllCQngtYXpOTm0yT3hWNDJZZFRWZE05SGFVbHVVTFFCZUJYa3hZUHBKY2hSUUI3Q1liY0JWYUNZREhJRmVMaUF2dFU1amhGd2diRFJEQlp6N3BjRHMyOUtTZ2xRR1NjRGxrV2JTYnY0WmhQeDhTUkc0VlNNMlkxUUFkLVY4MkdnOTRQMVJ5VGR4anRWQVhFZUZ4R2NmeWo5Vy1ZVTFhZUxQQ1VCWnZYTTZldWxlRUFpMGZLOXJEQXNhRmRua0RlRkNtUWF5eWVxZzRSR09mTnByalQ0NDBZX2RSR2hyWkNwc2t3a0dPbTUwMGNlakNDQks5TDhXS1A0d2pFUDJ3SVZuWG5JUDUxX1pxVTNsYVZjT1JYQWxHcGtFNVhJVHFCbHcifSwicmVsZWFzZV9wb2xpY3kiOnsiY29udGVudFR5cGUiOiJhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PXV0Zi04IiwiZGF0YSI6ImV5SmhibmxQWmlJNlczc2lZVzU1VDJZaU9sdDdJbU5zWVdsdElqb2ljMlJyTFhSbGMzUWlMQ0psY1hWaGJITWlPaUowY25WbEluMWRMQ0poZFhSb2IzSnBkSGtpT2lKb2RIUndjem92TDNOcmNtRjBkR1Z6ZEdGMGFXOXVMbUY2ZFhKbGQyVmljMmwwWlhNdWJtVjBMeUo5WFN3aWRtVnljMmx2YmlJNklqRXVNQzR3SW4wIn19fX0.eFUjCBDpkuWbRwBxJ23cBJtWnpe-n_4AKSpFNJGKwat7s8Ni8rZumtI8fQEYLDvCbNrDG2lGtTJYRjeNxvK0R11DhgBPJiNu-JJffnviTfG8usUAHX5awcWQ5BkENe-fCIi00YyWSKAdGqhq86gI7uzdYnKR8RxSbJzg6FQspfo7SNm5caaHN1t3oY6eJgWr3UN0-8MLVeuYPMP-Jz2OmVNpXwkyQ461k0aV7AzDszEuFvJ3kP-oGTfS8KuMU_xI-zAh8U83dWK0NHM21eRdXUvERdBSxjPxpLBkIjsBN9JOaemFp9LRdvxk6I5_To73o9292goc9BXD8_L_t3Rc2A\"}" + }, + "Exception" : null + } ], + "variables" : [ "keytorelease06795b" ] +} \ No newline at end of file diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyClientTest.releaseKey[1].json b/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyClientTest.releaseKey[1].json new file mode 100644 index 0000000000000..e55a999139b8c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/resources/session-records/KeyClientTest.releaseKey[1].json @@ -0,0 +1,52 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.managedhsm.azure.net/keys/3a5555a9-005b-4d3b-abb5-04481a5cd611-keyToRelease/create?api-version=7.3-preview", + "Headers" : { + "User-Agent" : "azsdk-java-client_name/client_version (11.0.6; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "content-length" : "1003", + "x-frame-options" : "SAMEORIGIN", + "retry-after" : "0", + "StatusCode" : "200", + "strict-transport-security" : "max-age=31536000; includeSubDomains", + "x-ms-server-latency" : "545", + "content-security-policy" : "default-src 'self'", + "x-content-type-options" : "nosniff", + "content-type" : "application/json; charset=utf-8", + "x-ms-keyvault-region" : "centralus", + "x-ms-keyvault-network-info" : "conn_type=Ipv4;addr=189.181.107.174;act_addr_fam=Ipv4;", + "cache-control" : "no-cache", + "x-ms-request-id" : "4fa0e8e6-f715-11eb-a622-000d3aa6a10f", + "Body" : "{\"attributes\":{\"created\":1628295646,\"enabled\":true,\"exportable\":true,\"recoverableDays\":90,\"recoveryLevel\":\"Recoverable+Purgeable\",\"updated\":1628295646},\"key\":{\"e\":\"AQAB\",\"key_ops\":[\"wrapKey\",\"decrypt\",\"encrypt\",\"unwrapKey\",\"sign\",\"verify\"],\"kid\":\"https://vicolinamhsmhsm.managedhsm.azure.net/keys/3a5555a9-005b-4d3b-abb5-04481a5cd611-keyToRelease/8713f6289a2441313075698b8625bf32\",\"kty\":\"RSA-HSM\",\"n\":\"qwmJOrLUYKn5h4NCpuYC1oFFPHms8nXM-z3rHWJK4OYBvqQLwhp6wBOhsm7FPenRxXzsZAZbjOaqUQ_sDkCWNulm_ifa6LsRvjM_4ZeAExP6kabgkeHU1nmAm0vi2aBKqbvgysKjexRsrHCHO9zxOtktOz9W8r4HMrNJZkAou-h5tuBzFfe5DrHsTLNv_BMFwmvwIkX_6Gg1Sahgo-26CZHl2Koi6GQyYS0-nVwvJzqUZXxNPslFFLO0DnCg65izEqx7KnCNJR0nIZ6UhkaiMqdZQ3JaZE1fXCdBrOE9s6MCz6Pk3g51rJ67biefhaaAaUEfTyDvBTiU0zSckh6IsQ\"},\"release_policy\":{\"contentType\":\"application/json; charset=utf-8\",\"data\":\"eyJhbnlPZiI6W3siYW55T2YiOlt7ImNsYWltIjoic2RrLXRlc3QiLCJlcXVhbHMiOiJ0cnVlIn1dLCJhdXRob3JpdHkiOiJodHRwczovL3NrcmF0dGVzdGF0aW9uLmF6dXJld2Vic2l0ZXMubmV0LyJ9XSwidmVyc2lvbiI6IjEuMC4wIn0\"}}" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.managedhsm.azure.net/keys/3a5555a9-005b-4d3b-abb5-04481a5cd611-keyToRelease//release?api-version=7.3-preview", + "Headers" : { + "User-Agent" : "azsdk-java-client_name/client_version (11.0.6; Windows 10; 10.0)", + "Content-Type" : "application/json" + }, + "Response" : { + "content-length" : "14161", + "x-frame-options" : "SAMEORIGIN", + "retry-after" : "0", + "StatusCode" : "200", + "strict-transport-security" : "max-age=31536000; includeSubDomains", + "x-ms-server-latency" : "389", + "content-security-policy" : "default-src 'self'", + "x-content-type-options" : "nosniff", + "content-type" : "application/json; charset=utf-8", + "x-ms-keyvault-region" : "centralus", + "x-ms-keyvault-network-info" : "conn_type=Ipv4;addr=189.181.107.174;act_addr_fam=Ipv4;", + "cache-control" : "no-cache", + "x-ms-request-id" : "50612eee-f715-11eb-a622-000d3aa6a10f", + "Body" : "{\"value\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6IjFJWjlZQ0gtRWo5OGJxcUZUSnRITHFtZUFXSm5WZkhEdFlUbC1LS0RkRnciLCJ4NWMiOlsiTUlJSW1qQ0NCb0tnQXdJQkFnSVRNd0FZS3BaWmV5L0l3TkxGN1FBQUFCZ3FsakFOQmdrcWhraUc5dzBCQVF3RkFEQlpNUXN3Q1FZRFZRUUdFd0pWVXpFZU1Cd0dBMVVFQ2hNVlRXbGpjbTl6YjJaMElFTnZjbkJ2Y21GMGFXOXVNU293S0FZRFZRUURFeUZOYVdOeWIzTnZablFnUVhwMWNtVWdWRXhUSUVsemMzVnBibWNnUTBFZ01EVXdIaGNOTWpFd09EQTJNak15TVRJeldoY05Nakl3T0RBeE1qTXlNVEl6V2pCOU1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQk1DVjBFeEVEQU9CZ05WQkFjVEIxSmxaRzF2Ym1ReEhqQWNCZ05WQkFvVEZVMXBZM0p2YzI5bWRDQkRiM0p3YjNKaGRHbHZiakV2TUMwR0ExVUVBd3dtS2k1MmFXTnZiR2x1WVcxb2MyMW9jMjB1YldGdVlXZGxaR2h6YlM1aGVuVnlaUzV1WlhRd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURMczZzL3RoZ2YzVk10WjBDeHpWVVAxYktReS9SVisvT1Y0MUxOcjdTMWtGYk9KaDJuLzhEZFNyWk5qN2dUbmxDTkJkV2ltWmY4ZHZwaUtMc0VMY2FlK01wQXk1OWhZYm96WGZyWTRuY2R6UVlsTXJGNHVMbUJicjF4MVMwWEtBMlQxTVByb3ZkbldXbDhuVkE4b2pPYklFdmlGN2F6MVppMmwyWmZpSHprQXo2YmhadmltUGNqdVRxTnpyVjJERHlXemdycVBSVzJaWDQyMUtNVUVTWFBObUhHNXZkN05kazYvc3ZpSjhubWVhRC8rSGRMcXpGVmFId3JvZ3lxSFFZNVk1VldSLzZGc1I0aE1wRzg5c2dITU1XSVZLVWVrdVBsNmhXb2haa0grdUN3Z29rdExKZ2EzRWN1VGtlbFJ5VlQ3RFcrSzJwS2sxcExLQ3JYS1I4YkFnTUJBQUdqZ2dRMU1JSUVNVENDQVg4R0Npc0dBUVFCMW5rQ0JBSUVnZ0Z2QklJQmF3RnBBSGNBN2t1OXQzWE9ZTHJoUW1rZnErR2VacU1QZmwrd2N0aURBTVI3aVhxby9jc0FBQUY3SGM2YUZBQUFCQU1BU0RCR0FpRUFuTUlydXp1cGFrK2tmWi84SE8xb0ZETEUvZ2RCS2NBWmZSbjBpMlJaVTBFQ0lRRGtuL2cyNkc4NWM2YmJDaHFnWnRwQjBaU1BZeG1ndUxmVEtyMWZHaXZVdEFCM0FDbDV2dkNlT1RraDhGWnpuMk9sZCtXK1YzMmNZQXI0K1UxZEpsd2xYY2VFQUFBQmV4M09ta1FBQUFRREFFZ3dSZ0loQUlLdUpHTm5YMEdzTWFNR2NHcnAxU3h0dUhiR2pSTUJscURVYkhPeEFHSXRBaUVBNE1DTkFuVVpVME1yNEdrZjB2K3l2TU13VUNBblVaQlZRbDNkZG9ZREtlVUFkUUJCeU1xeDN5SkdTaERHb1RvSlFvZGVUakdMR3dQcjYwdkhhUENRWXBZRzlnQUFBWHNkenBwdUFBQUVBd0JHTUVRQ0lHR1orM2pHNlcxM3ZVN3E0QWNsWU5kZHlVMVpHbzZSamNTK1M3Ui85VUszQWlCM0ZXSEdTNngrQlROR3hlYk1yVHBCbko0Y1ZFNGVyT0VQOEpmZTZOMng5VEFuQmdrckJnRUVBWUkzRlFvRUdqQVlNQW9HQ0NzR0FRVUZCd01DTUFvR0NDc0dBUVVGQndNQk1Ed0dDU3NHQVFRQmdqY1ZCd1F2TUMwR0pTc0dBUVFCZ2pjVkNJZTkxeHVCNSt0R2dvR2RMbzdRRElmdzJoMWRnb1RsYVlMenB6NENBV1FDQVNNd2dhNEdDQ3NHQVFVRkJ3RUJCSUdoTUlHZU1HMEdDQ3NHQVFVRkJ6QUNobUZvZEhSd09pOHZkM2QzTG0xcFkzSnZjMjltZEM1amIyMHZjR3RwYjNCekwyTmxjblJ6TDAxcFkzSnZjMjltZENVeU1FRjZkWEpsSlRJd1ZFeFRKVEl3U1hOemRXbHVaeVV5TUVOQkpUSXdNRFVsTWpBdEpUSXdlSE5wWjI0dVkzSjBNQzBHQ0NzR0FRVUZCekFCaGlGb2RIUndPaTh2YjI1bGIyTnpjQzV0YVdOeWIzTnZablF1WTI5dEwyOWpjM0F3SFFZRFZSME9CQllFRk1BRUlpT1JaMC9JbGx0SU80eG5ybWpEVlQ0cE1BNEdBMVVkRHdFQi93UUVBd0lFc0RCWEJnTlZIUkVFVURCT2dpWXFMblpwWTI5c2FXNWhiV2h6YldoemJTNXRZVzVoWjJWa2FITnRMbUY2ZFhKbExtNWxkSUlrZG1samIyeHBibUZ0YUhOdGFITnRMbTFoYm1GblpXUm9jMjB1WVhwMWNtVXVibVYwTUdRR0ExVWRId1JkTUZzd1dhQlhvRldHVTJoMGRIQTZMeTkzZDNjdWJXbGpjbTl6YjJaMExtTnZiUzl3YTJsdmNITXZZM0pzTDAxcFkzSnZjMjltZENVeU1FRjZkWEpsSlRJd1ZFeFRKVEl3U1hOemRXbHVaeVV5TUVOQkpUSXdNRFV1WTNKc01HWUdBMVVkSUFSZk1GMHdVUVlNS3dZQkJBR0NOMHlEZlFFQk1FRXdQd1lJS3dZQkJRVUhBZ0VXTTJoMGRIQTZMeTkzZDNjdWJXbGpjbTl6YjJaMExtTnZiUzl3YTJsdmNITXZSRzlqY3k5U1pYQnZjMmwwYjNKNUxtaDBiVEFJQmdabmdRd0JBZ0l3SHdZRFZSMGpCQmd3Rm9BVXg3S2NmeHpqdUZydjZXZ2FxRjJVd1NaU2FtZ3dIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0lHQ0NzR0FRVUZCd01CTUEwR0NTcUdTSWIzRFFFQkRBVUFBNElDQVFDbytOY205MUpSK1ozazg1WUorVmNtVTVjNkJ3ZGR0eTFFR2o4RWM0dmNDcmN1b2lVL1RFUW1PSUcrTWM4WXlYd2FuVHVxeW53UklFZUtsTmJUSXYyREwwbWhrREt1UzczNk5VdDVWMjV0RXFnck5mTWE4bWswT3dickoybjNhQ3hvVTlXNDFYNkJ6WVc0ckpGOXpxZnEzbUdhS3YxaUoyL2V0UmpSRE5XVkRQMVYvYTF3OC9WTndQUTJ3Y2dtOHNtdGwvY09nUWFHRzl2eEpFNGZjV2h3ajBscTRBcVZDYTBIWjJOZVNiTjZJdTNiNFBESVU4czJSeGFlVWFhTXNCWW9FVmQvOXV0TXpBQ25MRUlkSlNyQ2kzZlg1WEtHT0VpQWEwYUY5UXRCZ2JLdzB4ajZaT1R0RWRYZDhrdFhBdmI5TWxtNEFnM3RBRmlVRlRmSlJ4dCtyQVdjYzhOL0pXenUrSFRjdElHQnFldS95N2xmRUc4QWp6c2w3dEdIRGVTclBndWtqN204WGZXZmlleit2SjFXWnhKVTY2LzZIWnZyY25FOUsxb1dOWXZTNnBscG5HZFVaU21PVExVZVAzTEx5ZkNXUFBueDJnUC9IWnBCVldxQ3RXUkh6bm5oMU5mbWY3aEgvK3VERnA0ekZMQ0cwTTdCTXVrVnNBVkZCQ0dzRUJKM29sQ3I4NUdvc01GRFBxYit1VldVcjBWejZjNk1pNjhQVXphcSs1Ly9WWS82WWJwSDdyR24yTE9DRW5jNmlRUU1LN1JDM1NveVB3aG5rWC9LWitJWThBOUp5OUZpSnZBc2wwcUhlSXVrUSt2MGpqZ3N4U25MdGtmMjFNbUJvVU9JMTB1cEt0VDUzSDd0di95dU44cHdJTXQrRDRLaHphUmFDNUZic2c9PSIsIk1JSUY4ekNDQk51Z0F3SUJBZ0lRRFh2dDZYMkNDWlo2VW1NYmk5MFl2VEFOQmdrcWhraUc5dzBCQVF3RkFEQmhNUXN3Q1FZRFZRUUdFd0pWVXpFVk1CTUdBMVVFQ2hNTVJHbG5hVU5sY25RZ1NXNWpNUmt3RndZRFZRUUxFeEIzZDNjdVpHbG5hV05sY25RdVkyOXRNU0F3SGdZRFZRUURFeGRFYVdkcFEyVnlkQ0JIYkc5aVlXd2dVbTl2ZENCSE1qQWVGdzB5TURBM01qa3hNak13TURCYUZ3MHlOREEyTWpjeU16VTVOVGxhTUZreEN6QUpCZ05WQkFZVEFsVlRNUjR3SEFZRFZRUUtFeFZOYVdOeWIzTnZablFnUTI5eWNHOXlZWFJwYjI0eEtqQW9CZ05WQkFNVElVMXBZM0p2YzI5bWRDQkJlblZ5WlNCVVRGTWdTWE56ZFdsdVp5QkRRU0F3TlRDQ0FpSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBS3BsRFRtUTlhZndWUFFlbER1dStOa3hOSjA4NENOS25yWjIxQUJld0UrVVU0R0tEbnd5Z1pkSzZhZ05TTXM1VW9jaFVFRHp6OUNwZFY1dGRQekwxNE8vR2VFMmdPNS9hVUZUVU1HOWM2bmV5eGs1dHExV2RLc1BraXRQd3M2VjhNV2E1ZDFML3k0UkZoWkhVc2d4eFV5U2xZbEdwTmNIaGhzeXI3RXZGZWNaR0ExTWZzaXRBV1ZwNmhpV0FOa1dLSU5mUmNkdDNaMkEyM2htTUg5TVJTR0JjY0hpUHV6d3JWc1NtTHd2dDNXbFJEZ09iSmtFNDB0Rll2SjZHWEFRaWFHSENJV1NWT2JnTzN6ajZ4a2RiRUZNbUovenIyV2V0NUtFY1VEdFVCaEE0ZFVVb2FQVno2OXU0NlY1NlZzY3kzbFh1MVlsc2s4NGo1bFVQTGRzQXh0dWx0UDRPUFFvT1Rwblk4a3hXa0g2a2dPNWdUS0UzSFJ2b1ZJalU0eEowSlE3NDZ6eS84R2RRQTM2U2FOaXo0VTN1MTB6RlpnMlJrdjJkTDFMdjU4RVhMMDJyNXE1Qi9uaFZIL00xam9UdnBSdmFlRXBBSmhrSUE5TmtwdmJHRXBTZGNBME9ydE9PZUd0cnNpT3lNQllranBCNW53MGNKWTFRSE9yM25JdkoyT25ZK09LSmJEU3JoRnFXc2s4LzFxNloxV052T056N3RlMXBBdEhlcmRQaTVwQ0hlaVhDTnB2K2ZhZHdQMGs4Y3phZjJWczE5bllzZ1duNXVJeUxRTDhFZWhkQnpDYk9LSnk5c2w4NlM0RnFlNEhHeUF0bXFHbGFXT3NxMkE2Ty9wYU1pM0JTbVdURGJnUExDUEJiUHRlL2JzdUFFRjRhamtQRUVTM0dIUDlBZ01CQUFHamdnR3RNSUlCcVRBZEJnTlZIUTRFRmdRVXg3S2NmeHpqdUZydjZXZ2FxRjJVd1NaU2FtZ3dId1lEVlIwakJCZ3dGb0FVVGlKVUlCaVY1dU51NWcvNitya1M3UVlYanprd0RnWURWUjBQQVFIL0JBUURBZ0dHTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQkJnZ3JCZ0VGQlFjREFqQVNCZ05WSFJNQkFmOEVDREFHQVFIL0FnRUFNSFlHQ0NzR0FRVUZCd0VCQkdvd2FEQWtCZ2dyQmdFRkJRY3dBWVlZYUhSMGNEb3ZMMjlqYzNBdVpHbG5hV05sY25RdVkyOXRNRUFHQ0NzR0FRVUZCekFDaGpSb2RIUndPaTh2WTJGalpYSjBjeTVrYVdkcFkyVnlkQzVqYjIwdlJHbG5hVU5sY25SSGJHOWlZV3hTYjI5MFJ6SXVZM0owTUhzR0ExVWRId1IwTUhJd042QTFvRE9HTVdoMGRIQTZMeTlqY213ekxtUnBaMmxqWlhKMExtTnZiUzlFYVdkcFEyVnlkRWRzYjJKaGJGSnZiM1JITWk1amNtd3dONkExb0RPR01XaDBkSEE2THk5amNtdzBMbVJwWjJsalpYSjBMbU52YlM5RWFXZHBRMlZ5ZEVkc2IySmhiRkp2YjNSSE1pNWpjbXd3SFFZRFZSMGdCQll3RkRBSUJnWm5nUXdCQWdFd0NBWUdaNEVNQVFJQ01CQUdDU3NHQVFRQmdqY1ZBUVFEQWdFQU1BMEdDU3FHU0liM0RRRUJEQVVBQTRJQkFRQWUrRytHMlJGZFd0WXhMSUtNUjVIL2FWTkZqTlA3SmRldStvWmFLYUl1N1UzTmlkeWtGcjk5NGpTeE1CTVY3Njh1a0o1L2hMU0tzdWovU0xqbUFmd1JBWit3MFJHcWkva092UFlVbEJyL3NLT3dyM3RWa2c5Y2NaQmVibkJWRytETEtUcDJPeDArallCQ1B4bGE1Rk8yNTJxcGs3LzZ3dDhTWmszZGlTVTEySm03aWYvampraGtHQi9lOFVkZnJLb0x5dER2cVZlaXdQQTVGUHpxS29TcU43NWJ5TGpzSUtKRWROaTA3U1k0NWhOL1JVbnNtSW9BZjkzcWxhSFIvU0pXVlJocld0M0ptZW9CSjJSREs0OTJ6RjZUR3UxbW9oNGFFNmUwMFlrd1RQV3JldXd2YUxCMjIwdldtdGdaUHMrRFNJYjJkOWhQQmRDSmd2Y2hvMWM3IiwiTUlJRGpqQ0NBbmFnQXdJQkFnSVFBenJ4NXFjUnFhQzdLR1N4SFFuNjVUQU5CZ2txaGtpRzl3MEJBUXNGQURCaE1Rc3dDUVlEVlFRR0V3SlZVekVWTUJNR0ExVUVDaE1NUkdsbmFVTmxjblFnU1c1ak1Sa3dGd1lEVlFRTEV4QjNkM2N1WkdsbmFXTmxjblF1WTI5dE1TQXdIZ1lEVlFRREV4ZEVhV2RwUTJWeWRDQkhiRzlpWVd3Z1VtOXZkQ0JITWpBZUZ3MHhNekE0TURFeE1qQXdNREJhRncwek9EQXhNVFV4TWpBd01EQmFNR0V4Q3pBSkJnTlZCQVlUQWxWVE1SVXdFd1lEVlFRS0V3eEVhV2RwUTJWeWRDQkpibU14R1RBWEJnTlZCQXNURUhkM2R5NWthV2RwWTJWeWRDNWpiMjB4SURBZUJnTlZCQU1URjBScFoybERaWEowSUVkc2IySmhiQ0JTYjI5MElFY3lNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6Zk5OTng3YThteWFKQ3RTblgvUnJvaENnaU45UmxVeWZ1STIvT3U4anFKa1R4NjVxc0dHbXZQckMzb1hna2tSTHBpbW43V282aCs0RlIxSUFXc1VMZWNZeHBzTU56YUh4bXgxeDdlL2RmZ3k1U0RONjdzSDBOTzNYc3MwcjB1cFMva3FiaXRPdFNacExZbDZadHJBR0NTWVA5UElVa1k5MmVRcTJFR25JL3l1dW0wNlpJeWE3WHpWK2hkRzgyTUhhdVZCSlZKOHpVdGx1TkpiZDEzNC90SlM3U3NWUWVwajVXenRDTzdURzFGOFBhcHNwVXd0UDFNVll3blNsY1VmSUtkelhPUzB4WktCZ3lNVU5HUEhnbStGNkhtSWNyOWcrVVF2SU9sQ3NSbktQWnpGQlE5Um5iRGh4U0pJVFJOcnc5RkRLWkpvYnE3bk1XeE00TXBoUUlEQVFBQm8wSXdRREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTRHQTFVZER3RUIvd1FFQXdJQmhqQWRCZ05WSFE0RUZnUVVUaUpVSUJpVjV1TnU1Zy82K3JrUzdRWVhqemt3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUdCbktKUnZEa2hqNnpIZDZtY1kxWWw5UE1XTFNuL3B2dHNyRjkrd1gzTjNLaklUT1lGblFvUWo4a1ZuTmV5SXYvaVBzR0VNTktTdUlFeUV4dHY0TmVGMjJkK21RcnZIUkFpR2Z6WjBKRnJhYkEwVVdUVzk4a25kdGgvSnN3MUhLajJaTDd0Y3U3WFVJT0daWDFOR0ZkdG9tL0R6TU5VK01lS05oSjdqaXRyYWxqNDFFNlZmOFBsd1VIQkhRUkZYR1U3QWo2NEd4SlVURnk4YkpaOTE4ckdPbWFGdkU3RkJjZjZJS3NoUEVDQlYxL01VUmVYZ1JQVHFoNVV5a3c3K1UwYjZMSjMvaXlLNVM5a0pSYVRlcExpYVdOMGJmVktmamxsRGlJR2tuaWJWYjYzZERjWTNmZTBEa2h2bGQxOTI3anlOeEYxV1c2TFpabTZ6TlRmbE1yWT0iXSwieDV0I1MyNTYiOiIxSVo5WUNILUVqOThicXFGVEp0SExxbWVBV0puVmZIRHRZVGwtS0tEZEZ3In0.eyJyZXF1ZXN0Ijp7ImFwaS12ZXJzaW9uIjoiNy4zLXByZXZpZXciLCJlbmMiOiJDS01fUlNBX0FFU19LRVlfV1JBUCIsImtpZCI6Imh0dHBzOi8vdmljb2xpbmFtaHNtaHNtLm1hbmFnZWRoc20uYXp1cmUubmV0L2tleXMvM2E1NTU1YTktMDA1Yi00ZDNiLWFiYjUtMDQ0ODFhNWNkNjExLWtleVRvUmVsZWFzZSJ9LCJyZXNwb25zZSI6eyJrZXkiOnsiYXR0cmlidXRlcyI6eyJjcmVhdGVkIjoxNjI4Mjk1NjQ2LCJlbmFibGVkIjp0cnVlLCJleHBvcnRhYmxlIjp0cnVlLCJyZWNvdmVyYWJsZURheXMiOjkwLCJyZWNvdmVyeUxldmVsIjoiUmVjb3ZlcmFibGUrUHVyZ2VhYmxlIiwidXBkYXRlZCI6MTYyODI5NTY0Nn0sImtleSI6eyJlIjoiQVFBQiIsImtleV9oc20iOiJleUpqYVhCb1pYSjBaWGgwSWpvaVlrMVBTMFJuVm5GTU5XNVRVR0ZvVW1kc2JVeHpiekJRTmpGSWRXRmtSVmRFTUdGbVdqWjFaVTVMVnpKdlRrOHlWelZ2V2xCTk5XRTVSVGxrYkU1NFdYVldYM0JTTTNrNFltTjFSRWRhT1VkbFVWZFdOVjlqTjBKbFNDMUVhME50ZFd4WVJXaHlWVXBSVEdaV1JFWkVPSFF5YjBoMlIyVjJiMjFwWjE5SlJYVkVRblpyWlZOT00xaGtSR1ZIYWs1M01HWmFlUzE1YlVORFdTMDFTRVY2TVU5YVpsUmZabkJCYWpGSU5WTk9RVVZrTFhvdE9HazNUM2hTUVdGQ1YxUTRkV1l4ZWxKWGEzbENaMjkwU1VkclpsSkRWSGxOZVhsbmMwUTRXbU53ZUU1NmRXRk1iVWxDVUVwYVQyaHhTSEUxZEZCUFVtbFFNVnBYYm1WaGMycEpSbUZEZG10YWFsaE5XSGh5WXpsTWMySlNURkZqT0VSWGRWUnlMV1Z6V0ZwaGJYcFROMkpZTFU0eGJsVXpWWGRPVVVkbWVFSktjRUZDUzFsbU5ESlpOM05MZUdoMFltbFVVVEJPTW1wUWNIQTNXbTVpWVRGdFFrc3dVek5qUWtFd1dFZGhaVk5NVTFCRE5DMVdlbTlLTjA1b1pqSkdhRE5EZFMxcU4wTndOSFIzZGxVNWFrdGhibWRhVm5GblgwTm5jMWhLY0ZCVlkzRmhWbVZETjFCTk1uZHFVWFpOUlhGb0xUbE9jbU5CVkc1d01VbG1TMFpMYXpOSmJFcDBhbkYwYm1aSmFtYzBkMjU0VVd4eFZXOVJTVjlIWkZkeFZsSTJNRkJLVDNWWk4yNDFVSGd0YUZSUmExWktTV3REWjI1WFpqaEpXazV2VW1SalZFOUhkWEZhTFdRMmJtNDVPV0ZSZVY5UWVXaHFVRU5GV0ZaT1gwaEtSRWxrY1U5TVp6QjVNM1l4TUROcVgxbE1XRzVpTjJ0UlUwTjVZa1ZrUlU1a1dWbExVMTlDTTBnMk9VOUxUelJNVDNwTWJUWjZRMHhOZG1SaVEzZFJTak5FV0UxSWVsZE9WbHBZYTNOVWFISkRURmM1V1RZek5WVmtiamhxVmpSUVgwOUpNV0YzWm1oU1MyeGZSbmt4WVZseVMya3pjMHhzVTFWTVFqWlBlVXBXVlVkNlpEVlpTWHBCVjFkTVlsUlhjSFYxZW5SNlVuRnBWVmd3UWxKSk1YZzNSalE0Y3pGWlowRmFlakJ3TUVkTlowNUZjMnMxTW1RMFJHNXJaM2QzVVRKb1lYaGhSVGg2Y1RWVmFYWmthRmg1UldkVlpHcERjbEZhTFUxYVZ6Tm9kMWRaVlV4dk0yZGxSbUZWVlRSVVpsQnNVV1pWUlZGblQwSnFkMDlsVG5nME0zZHlUMnR0T0dNMVpIUktjRGhDVFVONWRVUjFRVlEwZDBKUmFVOXlabGN4VW5OWmREbHlXRXczVkhrMVdERldkMGM0WjBSdlZXeERjVWxrUTFaeGFsRkdlR2RRZFhKNGIxUmxaVTFTY1VWVlJsb3piekJqTFVONlFuUnJkRUZZVEc1T2VFdHRVMHRDTm5aWlVsZDBZMDV2V2pkTmRucGhkMEprYzNFM1dFRnZlbkZRWmpsNldVVnFkMVZ5YlVoTlJUbEVNVE15VW5BMVpGZzVVVE5QZGw5Mk16Rk5OVzQxTVc5MmRteGZaV2RaVW0xelpVVmtZMFo2Y0U5cVgzbHpjamg2V1VkWlMyZFJkMGROYUdoMVMyNXlZV0ZWZWpGRlJsTjZVM0JqYm5oTmN6bDJOa1E0YkMxaVgyeEZMVzVtT1U5VmFGUktPVE5KWVRseWFVaFNiMnh2U0VaUVVEVTNOVXhmZHpSNGRtSXRRa3BPVlhOMVdYUmtXbk5XWkZkb2IzUnNOR1pvWkhOd2JUWnVielZzTkZwMFN6aHBOSGxYTTB4dGNWQjVXbEZ4VjBGTFZWOUtUR0pWWWs1elZVaEROSFZ2YWxSVmFGWnNUek5NT0dwaWRVaFBhekpITUhGNU5XSXlNbk52Wm05cVRWVlBOWEZ4ZEVJM2RuWnlaMk5FYW5ZMWNqaDRNRmR3VlRkS1EzaEtOVzV2Tkd3MlNsSkpabkk0U0Rac0xYRm1aekpHTlZocFkyNU9ZVkE1VGw5TVNYcDVZUzFZZGxOQk5XdHdVVEpZUlhnM1JWRlhTMUZLTFdVeE9FOUhVMjB6VmxwdVVFZ3hUVWRLUTJGVU1IcDFVV3MwUXpWSVpFZzBXazFaY21obGNFZENWelpEWm5SUmNHUmZjbUZ5VVVKNWJVOHhRVWMzUlVGNk5EbHNZbXgxY1U1R1pGQkpha04wTUhvMU5VdEVaamR4Tm1oM1FqVmhjMGxTUVhFMldteE9WakptWkZOWVRITlpXREY2VDJGSVNYZG1kM0p3VlV3M1YxaEJRWHBIVTNoUWNGOVFiMWs1UVRaZlJ6UjRZMXBLTUdoQmRFUlZabWR6WDNOelRGWlRabkI2ZEdGc2NXdDNSVzFCVlhKSWNXZzRhbkp0Wlc5bmJGaDJiMUJVZUVOdFgxUlJkM1ZIYnpSNWRuVm9TemRLY3pCTk1VSXdlbGR0UTJkRWFuUkNWVWw2VjJsTVYzRnRSMlZ1U0dreVZqUXdkRFpWTkV4UVRqSnhZVWh6TWpGb1Qya3liVXRGTmpad1FYb3dWM2xUUldSSFUwc3haV3hJWkdvMlpURTJYekJHTlVWSldYTlRia3QxWVRWcWVIaFRkamx5WmpOT2RYSnFVMHR5ZDBOdFRqUmllV1o0TkhkU2JYSXRablo1ZEhoQ04zTjNlamxTT0ZORGNtNVFVbWhLU1hwaVZsTmtkRzlFWVRGVkxUUlFSWFE1Y2psSE4xSm5ZVjlIYzNGQ1gwcDRjeTFVYUdaR2VuTkZka2h4V0c5d1VFTk9aWEJaTkRkeVQxb3hORlZCTlVsdlkxSTNhV1JMYWpKUFZGSm5XWGhRUW5Cek9GVXhSbk5UYjNjeGQwTnNMWEZCYjBSMk1GcFlkVkZ6ZUdONmVGZFZOVmN4WVRkaFQwWmpSSFIwVGpScGIwbHJSMFZLYzIweFpEaGpMVVJ4WjB4ME9YVnJNMU4wVTJsQ1QwRkVOVXBOTjBObU9IbHFkRkF5TlZGbldrNVdNR05JVVdSemJ6VmhlSGxDU2xkelEwdHNOWFZhT1VGdFFuRTViSE5WWVU1WGMxSTRRekpUVlRab01XcEpWVVpETkhRMmFIUmZOR0poV1U1SVNscFlaV2xVUVZkTldIcFpka3hxUTJOZmEwMVVZbTF0VEZwdFJFMUxlamgyZFdkbWJrTnhTV3hLY0ZSaFpHeHpTMGxzVWxCUlNqUXpja3B2WldKS1gzbEhSMG8yTVU5Nk1Ea3lNSFE0U3poNVRUSjBkMlExU1U5UlVIVXhPSFZ3VlU1blRrRkJRbEU0T0ZNeU5tTkxiRlEyVG1WSU9EQnpjREpNVVhNNVVHeEVSRjlXYkd4UmVWRXplaUlzSW1obFlXUmxjaUk2ZXlKaGJHY2lPaUprYVhJaUxDSmxibU1pT2lKRFMwMWZVbE5CWDBGRlUxOUxSVmxmVjFKQlVDSXNJbXRwWkNJNkltWmhhMlV0Y21Wc1pXRnpaUzFyWlhraWZTd2ljMk5vWlcxaFgzWmxjbk5wYjI0aU9pSXhMakFpZlEiLCJrZXlfb3BzIjpbIndyYXBLZXkiLCJkZWNyeXB0IiwiZW5jcnlwdCIsInVud3JhcEtleSIsInNpZ24iLCJ2ZXJpZnkiXSwia2lkIjoiaHR0cHM6Ly92aWNvbGluYW1oc21oc20ubWFuYWdlZGhzbS5henVyZS5uZXQva2V5cy8zYTU1NTVhOS0wMDViLTRkM2ItYWJiNS0wNDQ4MWE1Y2Q2MTEta2V5VG9SZWxlYXNlLzg3MTNmNjI4OWEyNDQxMzEzMDc1Njk4Yjg2MjViZjMyIiwia3R5IjoiUlNBIiwibiI6InF3bUpPckxVWUtuNWg0TkNwdVlDMW9GRlBIbXM4blhNLXozckhXSks0T1lCdnFRTHdocDZ3Qk9oc203RlBlblJ4WHpzWkFaYmpPYXFVUV9zRGtDV051bG1faWZhNkxzUnZqTV80WmVBRXhQNmthYmdrZUhVMW5tQW0wdmkyYUJLcWJ2Z3lzS2pleFJzckhDSE85enhPdGt0T3o5VzhyNEhNck5KWmtBb3UtaDV0dUJ6RmZlNURySHNUTE52X0JNRndtdndJa1hfNkdnMVNhaGdvLTI2Q1pIbDJLb2k2R1F5WVMwLW5Wd3ZKenFVWlh4TlBzbEZGTE8wRG5DZzY1aXpFcXg3S25DTkpSMG5JWjZVaGthaU1xZFpRM0phWkUxZlhDZEJyT0U5czZNQ3o2UGszZzUxcko2N2JpZWZoYWFBYVVFZlR5RHZCVGlVMHpTY2toNklzUSJ9LCJyZWxlYXNlX3BvbGljeSI6eyJjb250ZW50VHlwZSI6ImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgiLCJkYXRhIjoiZXlKaGJubFBaaUk2VzNzaVlXNTVUMllpT2x0N0ltTnNZV2x0SWpvaWMyUnJMWFJsYzNRaUxDSmxjWFZoYkhNaU9pSjBjblZsSW4xZExDSmhkWFJvYjNKcGRIa2lPaUpvZEhSd2N6b3ZMM05yY21GMGRHVnpkR0YwYVc5dUxtRjZkWEpsZDJWaWMybDBaWE11Ym1WMEx5SjlYU3dpZG1WeWMybHZiaUk2SWpFdU1DNHdJbjAifX19fQ.Dn7guUG6V0PCU3MbKUTGiVjCi66NiqMhwBLIBB-f8dJ-9jbCK-wSars5lYo4ko0gG-NfC-iXUlt5eZxdaLCRplTbu0mvIxThWuuuBOvKI3kLj7gbvIMjHaoqlCFCrpqp8n9brRzR4j6-ufG6wrYVyJBpAbr3w54jtdCY2ydEPyTvJKDd4Q3ar5COdzab8i_6812CxQYiL1-BUkkKCd1OQ_iiD5TsO-Pt0TK6Anftb7Gr_WpAlH2N1Hj7F-A68mlO7JBdEOW5eA86eSUOtxR8E0Q8-uxaTmyu6UIBfqGZK5DyBfPxFyS8B4WzkcJcnM8sdTf2jFzQOuFuDtcDIzYWZQ\"}" + }, + "Exception" : null + } ], + "variables" : [ ] +} \ No newline at end of file diff --git a/sdk/keyvault/test-resources.json b/sdk/keyvault/test-resources.json index 0249123094529..8e3bade6d708a 100644 --- a/sdk/keyvault/test-resources.json +++ b/sdk/keyvault/test-resources.json @@ -120,6 +120,13 @@ "utcValue": { "type": "string", "defaultValue": "[utcNow()]" + }, + "attestationUrl": { + "type": "string", + "defaultValue": "https://skrattestation.azurewebsites.net/", + "metadata": { + "description": "Test attestation service for Secure Key Release" + } } }, "variables": { @@ -291,6 +298,7 @@ "enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]", "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]", "enableSoftDelete": true, + "softDeleteRetentionInDays": 7, "networkAcls": "[variables('networkAcls')]" } }, @@ -311,6 +319,7 @@ ], "enablePurgeProtection": false, "enableSoftDelete": true, + "softDeleteRetentionInDays": 7, "publicNetworkAccess": "Enabled", "networkAcls": { "bypass": "None", @@ -482,6 +491,10 @@ "BLOB_CONTAINER_NAME" : { "type": "string", "value": "[variables('blobContainerName')]" + }, + "AZURE_KEYVAULT_ATTESTATION_URL": { + "type": "string", + "value": "[parameters('attestationUrl')]" } } }