Skip to content

Commit

Permalink
Added fallback logic to use service-side cryptography if a key cannot…
Browse files Browse the repository at this point in the history
… be retrieved for local operations. (#38334)

* Added fallback logic to use service-side cryptography if a key cannot be retrieved for local operations.

* Updated CHANGELOG

* Fixed issue.

* Update sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md

Co-authored-by: Heath Stewart <heaths@microsoft.com>

* Improved error handling when receiving a 403 when fetching a key in the cryptography clients.

* Refactored how we initialize local crypto clients to avoid using async APIs.

* Fixed test issues and refactored internal local crypto clients.

* Updated test recordings.

* Fixed CheckStyle issues.

* Applied more PR feedback.

* Fixed a CheckStyle error.

* Added CheckStyle suppressions.

* Fixed NPE when using a local client.

* Update pom.xml

* Applied more PR feedback.

* Fixed CheckStyle issue.

* Applied more PR feedback.

* Updated CheckStyle suppressions.

* Updated code snippets.

---------

Co-authored-by: Heath Stewart <heaths@microsoft.com>
  • Loading branch information
vcolin7 and heaths authored Feb 21, 2024
1 parent 1f43591 commit 70d8722
Show file tree
Hide file tree
Showing 11 changed files with 844 additions and 1,109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -480,4 +480,9 @@ the main ServiceBusClientBuilder. -->

<!-- Suppress checks for defender Easm -->
<suppress checks="com.azure.tools.checkstyle.checks.EnforceFinalFieldsCheck" files="com.azure.analytics.defender.easm.models.CountPagedResponse"/>

<!-- Exceptions thrown here are caught in the calling method. -->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.keys.cryptography.CryptographyClient.java"
lines="1234,1237,1243"/>
</suppressions>
2 changes: 2 additions & 0 deletions sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Bugs Fixed

- Added fallback logic to use service-side cryptography if a key cannot be retrieved for local operations. ([#38334](https://github.com/Azure/azure-sdk-for-java/pull/38334))

### Other Changes

## 4.7.3 (2023-12-04)
Expand Down
2 changes: 1 addition & 1 deletion sdk/keyvault/azure-security-keyvault-keys/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/keyvault/azure-security-keyvault-keys",
"Tag": "java/keyvault/azure-security-keyvault-keys_c6d608b5d8"
"Tag": "java/keyvault/azure-security-keyvault-keys_b32c8c4df8"
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public final class CryptographyClientImpl {
public CryptographyClientImpl(String keyId, HttpPipeline pipeline, CryptographyServiceVersion serviceVersion) {
Objects.requireNonNull(keyId);

//Arrays.asList(vaultUrl, keyCollection, keyName, keyVersion);
List<String> data = unpackAndValidateId(keyId, LOGGER);

this.vaultUrl = data.get(0);
Expand Down Expand Up @@ -183,7 +182,6 @@ private EncryptResult encrypt(EncryptionAlgorithm algorithm, byte[] plainText, b
result.getAuthenticationTag(), result.getAdditionalAuthenticatedData());
}


public Mono<DecryptResult> decryptAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, Context context) {
Objects.requireNonNull(algorithm, "Encryption algorithm cannot be null.");
Objects.requireNonNull(ciphertext, "Ciphertext cannot be null.");
Expand Down Expand Up @@ -323,7 +321,6 @@ public UnwrapResult unwrapKey(KeyWrapAlgorithm algorithm, byte[] encryptedKey, C
return new UnwrapResult(result.getResult(), algorithm, keyId);
}


public Mono<SignResult> signDataAsync(SignatureAlgorithm algorithm, byte[] data, Context context) {
Objects.requireNonNull(algorithm, "Signature algorithm cannot be null.");
Objects.requireNonNull(data, "Data to be signed cannot be null.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
package com.azure.security.keyvault.keys.cryptography.implementation;

import com.azure.core.exception.HttpResponseException;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import com.azure.security.keyvault.keys.cryptography.models.EncryptionAlgorithm;
Expand All @@ -19,6 +20,7 @@
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import static com.azure.security.keyvault.keys.models.KeyType.EC;
Expand All @@ -32,6 +34,10 @@
* Utility methods for the Cryptography portion of KeyVault Keys.
*/
public final class CryptographyUtils {
private CryptographyUtils() {
// No-op
}

public static final String SECRETS_COLLECTION = "secrets";

public static List<String> unpackAndValidateId(String keyId, ClientLogger logger) {
Expand Down Expand Up @@ -66,32 +72,44 @@ public static List<String> unpackAndValidateId(String keyId, ClientLogger logger
}
}

public static LocalKeyCryptographyClient initializeCryptoClient(JsonWebKey jsonWebKey,
CryptographyClientImpl implClient, ClientLogger logger) {
public static LocalKeyCryptographyClient initializeLocalClient(JsonWebKey jsonWebKey,
CryptographyClientImpl implClient) {
if (!KeyType.values().contains(jsonWebKey.getKeyType())) {
throw logger.logExceptionAsError(new IllegalArgumentException(String.format(
"The JSON Web Key type: %s is not supported.", jsonWebKey.getKeyType().toString())));
throw new IllegalArgumentException(String.format(
"The JSON Web Key type: %s is not supported.", jsonWebKey.getKeyType().toString()));
}

try {
if (jsonWebKey.getKeyType().equals(RSA) || jsonWebKey.getKeyType().equals(RSA_HSM)) {
return new RsaKeyCryptographyClient(jsonWebKey, implClient);
} else if (jsonWebKey.getKeyType().equals(EC) || jsonWebKey.getKeyType().equals(EC_HSM)) {
return new EcKeyCryptographyClient(jsonWebKey, implClient);
} else if (jsonWebKey.getKeyType().equals(OCT) || jsonWebKey.getKeyType().equals(OCT_HSM)) {
return new AesKeyCryptographyClient(jsonWebKey, implClient);
}
} catch (RuntimeException e) {
throw logger.logExceptionAsError(new RuntimeException("Could not initialize local cryptography client.",
e));
if (jsonWebKey.getKeyType().equals(RSA) || jsonWebKey.getKeyType().equals(RSA_HSM)) {
return new RsaKeyCryptographyClient(jsonWebKey, implClient);
} else if (jsonWebKey.getKeyType().equals(EC) || jsonWebKey.getKeyType().equals(EC_HSM)) {
return new EcKeyCryptographyClient(jsonWebKey, implClient);
} else if (jsonWebKey.getKeyType().equals(OCT) || jsonWebKey.getKeyType().equals(OCT_HSM)) {
return new AesKeyCryptographyClient(jsonWebKey, implClient);
}

// Should not reach here.
return null;
// Should never reach this point.
throw new IllegalStateException("Could not create local cryptography client.");
}

public static boolean checkKeyPermissions(List<KeyOperation> operations, KeyOperation keyOperation) {
return operations.contains(keyOperation);
public static void verifyKeyPermissions(JsonWebKey jsonWebKey, KeyOperation keyOperation) {
if (!jsonWebKey.getKeyOps().contains(keyOperation)) {
throw new UnsupportedOperationException(
String.format("The %s operation is not allowed for key with id: %s",
keyOperation.toString().toLowerCase(Locale.ROOT), jsonWebKey.getId()));
}
}

public static boolean isThrowableRetryable(Throwable e) {
if (e instanceof HttpResponseException) {
int statusCode = ((HttpResponseException) e).getResponse().getStatusCode();

// Not a retriable error code.
return statusCode != 501 && statusCode != 505
&& (statusCode >= 500 || statusCode == 408 || statusCode == 429);
} else {
// Not a service-related transient error.
return false;
}
}

/*
Expand Down
Loading

0 comments on commit 70d8722

Please sign in to comment.