Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8a1f7f3
API updates
g2vinay Nov 12, 2019
9d757c8
samples update
g2vinay Nov 12, 2019
e4dc6ec
API changes
g2vinay Nov 13, 2019
3721edf
sample updates + checkstyle fixes
g2vinay Nov 15, 2019
d5ea025
feedback updates
g2vinay Nov 19, 2019
18e1e9a
compile fix
g2vinay Nov 19, 2019
3799f45
feedback update + sans update + checkstyle fixes
g2vinay Nov 19, 2019
82fa343
api updates
g2vinay Nov 25, 2019
c1bdc44
feedback updates
g2vinay Nov 26, 2019
2b17a83
feedback updates
g2vinay Nov 26, 2019
c4e7d9f
Merge branch 'certificates-api-changes' into certificates-api-consist…
g2vinay Nov 26, 2019
5baf4b9
javadoc update
g2vinay Nov 26, 2019
7efd1d4
checkstyle fix
g2vinay Nov 26, 2019
503b001
Merge branch 'master' into certificates-api-consistency-review-updates
g2vinay Nov 26, 2019
3562382
compile error fix
g2vinay Nov 26, 2019
638a630
code updates
g2vinay Dec 2, 2019
0f8853d
remove package-info file
g2vinay Dec 2, 2019
b775b9c
feedback + tests update
Dec 3, 2019
0994974
feedback updates
Dec 4, 2019
1b98e89
remove unwanted jacoco file
Dec 4, 2019
7ed1918
secret version update
Dec 4, 2019
6c419ff
Merge remote-tracking branch 'upstream/master' into secrets-version-u…
Dec 4, 2019
674859c
version file update
Dec 4, 2019
717e226
pom version updates
Dec 4, 2019
af3ae7d
add tests
Dec 5, 2019
d402d08
tests update
Dec 5, 2019
21c1d52
drop codecs dependency
Dec 5, 2019
aef7f48
update base 64 usage
Dec 5, 2019
698826a
Merge remote-tracking branch 'upstream/master' into kv-keys-updates
Dec 5, 2019
20c9239
spotbugs fix
Dec 5, 2019
f8c2611
feedback update
Dec 5, 2019
6e91dd5
code updates
Dec 6, 2019
65de4fd
Merge remote-tracking branch 'upstream/master' into kv-keys-updates
Dec 6, 2019
f13b8fd
build ifx
Dec 6, 2019
2b16d55
changelog + tests update
Dec 6, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@
<Class name="com.azure.security.keyvault.secrets.models.KeyVaultSecret"/>
<Class name="com.azure.security.keyvault.secrets.models.SecretProperties"/>
<Class name="com.azure.security.keyvault.keys.models.DeletedKey"/>
<Class name="com.azure.security.keyvault.keys.cryptography.SecretKey"/>
<Class name="com.azure.security.keyvault.keys.models.KeyVaultKey"/>
<Class name="com.azure.security.keyvault.keys.models.KeyProperties"/>
<Class name="com.azure.security.keyvault.certificates.models.DeletedCertificate"/>
Expand Down Expand Up @@ -531,6 +532,10 @@
<Class name="com.azure.security.keyvault.keys.models.ByteExtensions"/>
<Method name="clone"/>
</And>
<And>
<Class name="com.azure.security.keyvault.keys.models.KeyProperties"/>
<Method name="decode"/>
</And>
</Or>
<Bug pattern="PZLA_PREFER_ZERO_LENGTH_ARRAYS"/>
</Match>
Expand Down
7 changes: 6 additions & 1 deletion sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release History
## 4.0.0 (2019-10-31)
## 4.0.1 (2019-12-06)

### Major changes
- `KeyEncryptionKeyClientBuilder.buildKeyEncryptionKey` and `KeyEncryptionKeyClientBuilder.buildAsyncKeyEncryptionKey`supports consumption of a secret id representing the symmetric key stored in the Key Vault as a secret.
- Dropped third party dependency on apache commons codec library.


### Breaking changes
- Key has been renamed to KeyVaultKey to avoid ambiguity with other libraries and to yield better search results.
Expand Down
6 changes: 0 additions & 6 deletions sdk/keyvault/azure-security-keyvault-keys/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@
<version>1.1.0</version> <!-- {x-version-update;com.azure:azure-core-http-netty;dependency} -->
</dependency>

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version> <!-- {x-version-update;commons-codec:commons-codec;external_dependency} -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove this library from the external_dependencies.txt too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to check if any other SDK still has this dependency.
Best to clean that up in a follow up PR.

</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@
@ServiceClient(builder = CryptographyClientBuilder.class, isAsync = true, serviceInterfaces = CryptographyService.class)
public class CryptographyAsyncClient {
static final String KEY_VAULT_SCOPE = "https://vault.azure.net/.default";
static final String SECRETS_COLLECTION = "secrets";
JsonWebKey key;
private final CryptographyService service;
private final CryptographyServiceClient cryptographyServiceClient;
private CryptographyServiceClient cryptographyServiceClient;
private LocalKeyCryptographyClient localKeyCryptographyClient;
private final ClientLogger logger = new ClientLogger(CryptographyAsyncClient.class);
private String keyCollection;

/**
* Creates a CryptographyAsyncClient that uses {@code pipeline} to service requests
Expand Down Expand Up @@ -168,6 +170,14 @@ Mono<Response<KeyVaultKey>> getKeyWithResponse(Context context) {
return cryptographyServiceClient.getKey(context);
}

Mono<JsonWebKey> getSecretKey() {
try {
return withContext(context -> cryptographyServiceClient.getSecretKey(context)).flatMap(FluxUtil::toMono);
} catch (RuntimeException ex) {
return monoError(logger, ex);
}
}

/**
* Encrypts an arbitrary sequence of bytes using the configured key. Note that the encrypt operation only supports a
* single block of data, the size of which is dependent on the target key and the encryption algorithm to be used.
Expand Down Expand Up @@ -591,6 +601,7 @@ private void unpackAndValidateId(String keyId) {
String endpoint = url.getProtocol() + "://" + url.getHost();
String keyName = (tokens.length >= 3 ? tokens[2] : null);
String version = (tokens.length >= 4 ? tokens[3] : null);
this.keyCollection = (tokens.length >= 2 ? tokens[1] : null);
if (Strings.isNullOrEmpty(endpoint)) {
throw logger.logExceptionAsError(new IllegalArgumentException("Key endpoint in key id is invalid"));
} else if (Strings.isNullOrEmpty(keyName)) {
Expand All @@ -609,10 +620,14 @@ private boolean checkKeyPermissions(List<KeyOperation> operations, KeyOperation

private boolean ensureValidKeyAvailable() {
boolean keyAvailableLocally = true;
if (this.key == null) {
if (this.key == null && keyCollection != null) {
try {
KeyVaultKey keyVaultKey = getKey().block();
this.key = keyVaultKey.getKey();
if (keyCollection.equals(SECRETS_COLLECTION)) {
this.key = getSecretKey().block();
} else {
KeyVaultKey keyVaultKey = getKey().block();
this.key = keyVaultKey.getKey();
}
keyAvailableLocally = this.key.isValid();
initializeCryptoClients();
} catch (HttpResponseException | NullPointerException e) {
Expand All @@ -627,4 +642,8 @@ private boolean ensureValidKeyAvailable() {
CryptographyServiceClient getCryptographyServiceClient() {
return cryptographyServiceClient;
}

void setCryptographyServiceClient(CryptographyServiceClient serviceClient) {
this.cryptographyServiceClient = serviceClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.azure.security.keyvault.keys.cryptography;

import com.azure.core.annotation.Put;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.exception.ResourceModifiedException;
import com.azure.core.exception.ResourceNotFoundException;
Expand Down Expand Up @@ -130,4 +131,29 @@ Mono<Response<KeyVaultKey>> getKey(@HostParam("url") String url,
@HeaderParam("accept-language") String acceptLanguage,
@HeaderParam("Content-Type") String type,
Context context);

@Get("secrets/{secret-name}/{secret-version}")
@ExpectedResponses({200})
@UnexpectedResponseExceptionType(code = {404}, value = ResourceNotFoundException.class)
@UnexpectedResponseExceptionType(code = {403}, value = ResourceModifiedException.class)
@UnexpectedResponseExceptionType(HttpResponseException.class)
Mono<Response<SecretKey>> getSecret(@HostParam("url") String url,
@PathParam("secret-name") String keyName,
@PathParam("secret-version") String keyVersion,
@QueryParam("api-version") String apiVersion,
@HeaderParam("accept-language") String acceptLanguage,
@HeaderParam("Content-Type") String type,
Context context);

@Put("secrets/{secret-name}")
@ExpectedResponses({200})
@UnexpectedResponseExceptionType(code = {400}, value = ResourceModifiedException.class)
@UnexpectedResponseExceptionType(HttpResponseException.class)
Mono<Response<SecretKey>> setSecret(@HostParam("url") String url,
@PathParam("secret-name") String secretName,
@QueryParam("api-version") String apiVersion,
@HeaderParam("accept-language") String acceptLanguage,
@BodyParam("body") SecretRequestParameters parameters,
@HeaderParam("Content-Type") String type,
Context context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package com.azure.security.keyvault.keys.cryptography;

import com.azure.core.http.rest.Response;
import com.azure.core.http.rest.SimpleResponse;
import com.azure.core.util.Context;
import com.azure.core.util.logging.ClientLogger;
import com.azure.security.keyvault.keys.cryptography.models.DecryptResult;
Expand All @@ -15,13 +16,22 @@
import com.azure.security.keyvault.keys.cryptography.models.SignResult;
import com.azure.security.keyvault.keys.cryptography.models.VerifyResult;
import com.azure.security.keyvault.keys.cryptography.models.WrapResult;
import com.azure.security.keyvault.keys.models.JsonWebKey;
import com.azure.security.keyvault.keys.models.KeyOperation;
import com.azure.security.keyvault.keys.models.KeyType;
import com.azure.security.keyvault.keys.models.KeyVaultKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import reactor.core.publisher.Mono;

import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Objects;

class CryptographyServiceClient {
Expand Down Expand Up @@ -57,6 +67,56 @@ private Mono<Response<KeyVaultKey>> getKey(String name, String version, Context
.doOnError(error -> logger.warning("Failed to get key - {}", name, error));
}

Mono<Response<JsonWebKey>> getSecretKey(Context context) {
return service.getSecret(vaultUrl, keyName, version, API_VERSION, ACCEPT_LANGUAGE, CONTENT_TYPE_HEADER_VALUE, context)
.doOnRequest(ignored -> logger.info("Retrieving key - {}", keyName))
.doOnSuccess(response -> logger.info("Retrieved key - {}", response.getValue().getName()))
.doOnError(error -> logger.warning("Failed to get key - {}", keyName, error))
.flatMap((stringResponse -> {
KeyVaultKey key = null;
try {
return Mono.just(new SimpleResponse<>(stringResponse.getRequest(),
stringResponse.getStatusCode(),
stringResponse.getHeaders(), transformSecretKey(stringResponse.getValue())));
} catch (JsonProcessingException e) {
return Mono.error(e);
}
}));
}

Mono<Response<SecretKey>> setSecretKey(SecretKey secret, Context context) {
Objects.requireNonNull(secret, "The Secret input parameter cannot be null.");
SecretRequestParameters parameters = new SecretRequestParameters()
.setValue(secret.getValue())
.setTags(secret.getProperties().getTags())
.setContentType(secret.getProperties().getContentType())
.setSecretAttributes(new SecretRequestAttributes(secret.getProperties()));

return service.setSecret(vaultUrl, secret.getName(), API_VERSION, ACCEPT_LANGUAGE, parameters,
CONTENT_TYPE_HEADER_VALUE, context)
.doOnRequest(ignored -> logger.info("Setting secret - {}", secret.getName()))
.doOnSuccess(response -> logger.info("Set secret - {}", response.getValue().getName()))
.doOnError(error -> logger.warning("Failed to set secret - {}", secret.getName(), error));
}

JsonWebKey transformSecretKey(SecretKey secretKey) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.createObjectNode();
ArrayNode a = mapper.createArrayNode();
a.add(KeyOperation.WRAP_KEY.toString());
a.add(KeyOperation.UNWRAP_KEY.toString());
a.add(KeyOperation.ENCRYPT.toString());
a.add(KeyOperation.DECRYPT.toString());

((ObjectNode) rootNode).put("k", Base64.getUrlDecoder().decode(secretKey.getValue()));
((ObjectNode) rootNode).put("kid", this.keyId);
((ObjectNode) rootNode).put("kty", KeyType.OCT.toString());
((ObjectNode) rootNode).put("key_ops", a);

String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
return mapper.readValue(jsonString, JsonWebKey.class);
}

Mono<EncryptResult> encrypt(EncryptionAlgorithm algorithm, byte[] plaintext, Context context) {

KeyOperationParameters parameters = new KeyOperationParameters().setAlgorithm(algorithm).setValue(plaintext);
Expand Down Expand Up @@ -176,6 +236,4 @@ private void unpackId(String keyId) {
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ public byte[] wrapKey(String algorithm, byte[] key) {
public byte[] unwrapKey(String algorithm, byte[] encryptedKey) {
return client.unwrapKey(algorithm, encryptedKey).block();
}

KeyEncryptionKeyAsyncClient getKeyEncryptionKeyAsyncClient() {
return client;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Map;
import java.util.Objects;


class SecretKey {

/*
* The value of the secret.
*/
@JsonProperty(value = "value")
private String value;

/*
* The secret properties.
*/
private SecretProperties properties;

/*
* Creates an empty instance of the Secret.
*/
SecretKey() {
properties = new SecretProperties();
}

/*
* Creates a Secret with {@code name} and {@code value}.
*
* @param name The name of the secret.
* @param value the value of the secret.
*/
SecretKey(String name, String value) {
properties = new SecretProperties(name);
this.value = value;
}

/*
* Get the value of the secret.
*
* @return the secret value
*/
String getValue() {
return this.value;
}

/*
* Get the secret identifier.
*
* @return the secret identifier.
*/
String getId() {
return properties.getId();
}

/*
* Get the secret name.
*
* @return the secret name.
*/
String getName() {
return properties.getName();
}

/*
* Get the secret properties
* @return the Secret properties
*/
SecretProperties getProperties() {
return this.properties;
}

/*
* Set the secret properties
* @param properties The Secret properties
* @throws NullPointerException if {@code properties} is null.
* @return the updated secret key object
*/
SecretKey setProperties(SecretProperties properties) {
Objects.requireNonNull(properties);
properties.name = this.properties.name;
this.properties = properties;
return this;
}

@JsonProperty(value = "id")
private void unpackId(String id) {
properties.unpackId(id);
}

/*
* Unpacks the attributes json response and updates the variables in the Secret Attributes object.
* Uses Lazy Update to set values for variables id, tags, contentType, managed and keyId as these variables are
* part of main json body and not attributes json body when the secret response comes from list Secrets operations.
* @param attributes The key value mapping of the Secret attributes
*/
@JsonProperty("attributes")
@SuppressWarnings("unchecked")
private void unpackAttributes(Map<String, Object> attributes) {
properties.unpackAttributes(attributes);
}

@JsonProperty("managed")
private void unpackManaged(Boolean managed) {
properties.managed = managed;
}

@JsonProperty("kid")
private void unpackKid(String kid) {
properties.keyId = kid;
}

@JsonProperty("contentType")
private void unpackContentType(String contentType) {
properties.contentType = contentType;
}

@JsonProperty("tags")
private void unpackTags(Map<String, String> tags) {
properties.tags = tags;
}
}

Loading