Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrated Key Vault JCA library to use azure-json serialization/deserialization #41508

Merged
merged 15 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions sdk/keyvault/azure-security-keyvault-jca/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,11 @@
<scope>provided</scope>
<version>2.5.2</version> <!-- {x-version-update;org.conscrypt:conscrypt-openjdk-uber;external_dependency} -->
</dependency>
<!-- Jackson Databind -->
<!-- Azure JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version> <!-- {x-version-update;com.fasterxml.jackson.core:jackson-databind;external_dependency} -->
<optional>true</optional>
<groupId>com.azure</groupId>
<artifactId>azure-json</artifactId>
<version>1.2.0</version> <!-- {x-version-update;com.azure:azure-json;dependency} -->
</dependency>
<!-- SLF4j -->
<dependency>
Expand Down Expand Up @@ -153,15 +152,8 @@
</filters>
<relocations>
<relocation>
<pattern>com.fasterxml.jackson</pattern>
<shadedPattern>com.azure.security.keyvault.jca.implementation.shaded.com.fasterxml.jackson</shadedPattern>
<excludes>
<exclude>com/fasterxml/jackson/databind/jsonFormatVisitors/**</exclude> <!-- Avoid upper case in package name-->
</excludes>
</relocation>
<relocation>
<pattern>com.fasterxml.jackson.databind.jsonFormatVisitors</pattern>
<shadedPattern>com.azure.security.keyvault.jca.implementation.shaded.com.fasterxml.jackson.databind.json.format.visitors</shadedPattern> <!-- Avoid upper case in package name-->
<pattern>com.azure.json</pattern>
<shadedPattern>com.azure.security.keyvault.jca.implementation.shaded.com.azure.json</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons</pattern>
Expand All @@ -187,6 +179,15 @@
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<artifact>com.azure:azure-json</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
Expand All @@ -205,9 +206,7 @@
<configuration>
<overwriteExistingFiles>true</overwriteExistingFiles>
<module>
<moduleInfoFile>
src/main/resources/module-info.java
</moduleInfoFile>
<moduleInfoFile>src/main/resources/module-info.java</moduleInfoFile>
</module>
</configuration>
</execution>
Expand Down Expand Up @@ -242,7 +241,6 @@
<bannedDependencies>
<includes>
<include>org.bouncycastle:bcpkix-lts8on:[2.73.6]</include> <!-- {x-include-update;org.bouncycastle:bcpkix-lts8on;external_dependency} -->
<include>com.fasterxml.jackson.core:jackson-databind:[2.17.2]</include> <!-- {x-include-update;com.fasterxml.jackson.core:jackson-databind;external_dependency} -->
<include>org.conscrypt:conscrypt-openjdk-uber:[2.5.2]</include> <!-- {x-include-update;org.conscrypt:conscrypt-openjdk-uber;external_dependency} -->
<include>org.apache.httpcomponents:httpclient:[4.5.14]</include> <!-- {x-include-update;org.apache.httpcomponents:httpclient;external_dependency} -->
<include>org.slf4j:slf4j-nop:[1.7.36]</include> <!-- {x-include-update;org.slf4j:slf4j-nop;external_dependency} -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.Key;
import java.security.KeyFactory;
Expand Down Expand Up @@ -202,8 +203,8 @@ private AccessToken getAccessTokenByHttpRequest() {
} else {
accessToken = AccessTokenUtil.getAccessToken(resource, managedIdentity);
}
} catch (Throwable t) {
LOGGER.log(WARNING, "Could not obtain access token to authenticate with.", t);
} catch (UnsupportedEncodingException e) {
LOGGER.log(WARNING, "Could not obtain access token to authenticate with.", e);
}

LOGGER.exiting("KeyVaultClient", "getAccessTokenByHttpRequest", accessToken);
Expand All @@ -217,6 +218,8 @@ private AccessToken getAccessTokenByHttpRequest() {
* @return The list of aliases.
*/
public List<String> getAliases() {
LOGGER.entering("KeyVaultClient", "getAliases");

ArrayList<String> result = new ArrayList<>();
HashMap<String, String> headers = new HashMap<>();

Expand All @@ -229,8 +232,11 @@ public List<String> getAliases() {
CertificateListResult certificateListResult = null;

if (response != null) {
certificateListResult
= (CertificateListResult) JsonConverterUtil.fromJson(response, CertificateListResult.class);
try {
certificateListResult = JsonConverterUtil.fromJson(CertificateListResult::fromJson, response);
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to parse certificate list response", e);
}
}

if (certificateListResult != null) {
Expand All @@ -245,6 +251,8 @@ public List<String> getAliases() {
}
}

LOGGER.exiting("KeyVaultClient", "getAliases", result);

return result;
}

Expand All @@ -255,6 +263,8 @@ public List<String> getAliases() {
* @return The certificate bundle.
*/
private CertificateBundle getCertificateBundle(String alias) {
LOGGER.entering("KeyVaultClient", "getCertificateBundle", alias);

CertificateBundle result = null;
HashMap<String, String> headers = new HashMap<>();

Expand All @@ -264,9 +274,15 @@ private CertificateBundle getCertificateBundle(String alias) {
String response = HttpUtil.get(uri, headers);

if (response != null) {
result = (CertificateBundle) JsonConverterUtil.fromJson(response, CertificateBundle.class);
try {
result = JsonConverterUtil.fromJson(CertificateBundle::fromJson, response);
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to parse certificate bundle response", e);
}
}

LOGGER.exiting("KeyVaultClient", "getCertificateBundle", result);

return result;
}

Expand Down Expand Up @@ -315,22 +331,35 @@ public Certificate[] getCertificateChain(String alias) {
LOGGER.log(INFO, "Getting certificate chain for alias: {0}", alias);

HashMap<String, String> headers = new HashMap<>();

headers.put("Authorization", "Bearer " + getAccessToken());

String uri = keyVaultUri + "secrets/" + alias + API_VERSION_POSTFIX;
String response = HttpUtil.get(uri, headers);

if (response == null) {
throw new NullPointerException();
}
SecretBundle secretBundle = (SecretBundle) JsonConverterUtil.fromJson(response, SecretBundle.class);

SecretBundle secretBundle = null;

try {
secretBundle = JsonConverterUtil.fromJson(SecretBundle::fromJson, response);
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to parse secret bundle response", e);
}

Certificate[] certificates = new Certificate[0];

try {
certificates = loadCertificatesFromSecretBundleValue(secretBundle.getValue());
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException
| NoSuchProviderException | PKCSException e) {
LOGGER.log(WARNING, "Unable to decode certificate chain", e);
}

LOGGER.exiting("KeyVaultClient", "getCertificate", alias);

return certificates;
}

Expand Down Expand Up @@ -362,14 +391,16 @@ public Key getKey(String alias, char[] password) {
// Return KeyVaultPrivateKey if certificate is not exportable because if the service needs to obtain the
// private key for authentication, and we can't access private key(which is not exportable), we will use
// the Azure Key Vault Secrets API to obtain the private key (keyless).
LOGGER.exiting("KeyVaultClient", "getKey", null);

String keyType2 = keyType.contains("-HSM") ? keyType.substring(0, keyType.indexOf("-HSM")) : keyType;

return Optional.ofNullable(certificateBundle)
KeyVaultPrivateKey key = Optional.ofNullable(certificateBundle)
.map(CertificateBundle::getKid)
.map(kid -> new KeyVaultPrivateKey(keyType2, kid, this))
.orElse(null);

LOGGER.exiting("KeyVaultClient", "getKey", key);

return key;
}

String certificateSecretUri = certificateBundle.getSid();
Expand All @@ -394,8 +425,15 @@ public Key getKey(String alias, char[] password) {
// If the certificate is exportable the private key is available, so we'll store the private key for
// authentication instead of obtaining a digital signature through the API (without keyless).
Key key = null;
SecretBundle secretBundle = (SecretBundle) JsonConverterUtil.fromJson(body, SecretBundle.class);
String contentType = secretBundle.getContentType();
SecretBundle secretBundle = null;
String contentType = null;

try {
secretBundle = JsonConverterUtil.fromJson(SecretBundle::fromJson, body);
contentType = secretBundle.getContentType();
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to parse secret bundle response.", e);
}

if ("application/x-pkcs12".equals(contentType)) {
try {
Expand All @@ -414,14 +452,13 @@ public Key getKey(String alias, char[] password) {
} else if ("application/x-pem-file".equals(contentType)) {
try {
key = createPrivateKeyFromPem(secretBundle.getValue(), keyType);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException ex) {
LOGGER.log(WARNING, "Unable to decode key", ex);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e) {
LOGGER.log(WARNING, "Unable to decode key", e);
}
}

// If the private key is not available the certificate cannot be
// used for server side certificates or mTLS. Then we do not know
// the intent of the usage at this stage we skip this key.
// If the private key is not available the certificate cannot be used for server side certificates or mTLS.
// Then we do not know the intent of the usage at this stage we skip this key.
LOGGER.exiting("KeyVaultClient", "getKey", key);

return key;
Expand All @@ -437,6 +474,8 @@ public Key getKey(String alias, char[] password) {
* @return Signature.
*/
public byte[] getSignedWithPrivateKey(String digestName, String digestValue, String keyId) {
LOGGER.entering("KeyVaultClient", "getSignedWithPrivateKey", new Object[] { digestName, digestValue, keyId });

SignResult result = null;
String bodyString = String.format("{\"alg\": \"" + digestName + "\", \"value\": \"%s\"}", digestValue);
Map<String, String> headers = new HashMap<>();
Expand All @@ -447,14 +486,24 @@ public byte[] getSignedWithPrivateKey(String digestName, String digestValue, Str
String response = HttpUtil.post(uri, headers, bodyString, "application/json");

if (response != null) {
result = (SignResult) JsonConverterUtil.fromJson(response, SignResult.class);
try {
result = JsonConverterUtil.fromJson(SignResult::fromJson, response);
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to parse sign result response.", e);
}
}

byte[] signature;

if (result != null) {
return Base64.getUrlDecoder().decode(result.getValue());
signature = Base64.getUrlDecoder().decode(result.getValue());
} else {
signature = new byte[0];
}

return new byte[0];
LOGGER.exiting("KeyVaultClient", "getSignedWithPrivateKey", signature);

return signature;
}

/**
Expand All @@ -472,6 +521,8 @@ public byte[] getSignedWithPrivateKey(String digestName, String digestValue, Str
private PrivateKey createPrivateKeyFromPem(String pemString, String keyType)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {

LOGGER.entering("KeyVaultClient", "createPrivateKeyFromPem", new Object[] { pemString, keyType });

StringBuilder builder = new StringBuilder();

try (BufferedReader reader = new BufferedReader(new StringReader(pemString))) {
Expand All @@ -496,7 +547,10 @@ private PrivateKey createPrivateKeyFromPem(String pemString, String keyType)
byte[] bytes = Base64.getDecoder().decode(builder.toString());
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(keyType);
PrivateKey privateKey = factory.generatePrivate(spec);

LOGGER.exiting("KeyVaultClient", "createPrivateKeyFromPem", privateKey);

return factory.generatePrivate(spec);
return privateKey;
}
}
Loading
Loading