diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index c3553c00bd415..6e9df881c8503 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -18,6 +18,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; @@ -199,8 +200,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); @@ -214,6 +215,8 @@ private AccessToken getAccessTokenByHttpRequest() { * @return The list of aliases. */ public List getAliases() { + LOGGER.entering("KeyVaultClient", "getAliases"); + ArrayList result = new ArrayList<>(); HashMap headers = new HashMap<>(); @@ -227,10 +230,9 @@ public List getAliases() { if (response != null) { try { - certificateListResult = - (CertificateListResult) JsonConverterUtil.fromJson(CertificateListResult.class, response); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse certificate list response"); + certificateListResult = JsonConverterUtil.fromJson(CertificateListResult.class, response); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse certificate list response", e); } } @@ -248,6 +250,8 @@ public List getAliases() { } } + LOGGER.exiting("KeyVaultClient", "getAliases", result); + return result; } @@ -258,6 +262,8 @@ public List getAliases() { * @return The certificate bundle. */ private CertificateBundle getCertificateBundle(String alias) { + LOGGER.entering("KeyVaultClient", "getCertificateBundle", alias); + CertificateBundle result = null; HashMap headers = new HashMap<>(); @@ -268,12 +274,14 @@ private CertificateBundle getCertificateBundle(String alias) { if (response != null) { try { - result = (CertificateBundle) JsonConverterUtil.fromJson(CertificateBundle.class, response); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse certificate bundle response"); + result = JsonConverterUtil.fromJson(CertificateBundle.class, response); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse certificate bundle response", e); } } + LOGGER.exiting("KeyVaultClient", "getCertificateBundle", result); + return result; } @@ -338,14 +346,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(); @@ -374,10 +384,10 @@ public Key getKey(String alias, char[] password) { String contentType = null; try { - secretBundle = (SecretBundle) JsonConverterUtil.fromJson(SecretBundle.class, body); + secretBundle = JsonConverterUtil.fromJson(SecretBundle.class, body); contentType = secretBundle.getContentType(); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse secret bundle response"); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse secret bundle response.", e); } if ("application/x-pkcs12".equals(contentType)) { @@ -397,8 +407,8 @@ 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); } } @@ -419,6 +429,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 headers = new HashMap<>(); @@ -430,17 +442,23 @@ public byte[] getSignedWithPrivateKey(String digestName, String digestValue, Str if (response != null) { try { - result = (SignResult) JsonConverterUtil.fromJson(SignResult.class, response); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse sign result response"); + result = JsonConverterUtil.fromJson(SignResult.class, 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; } /** @@ -458,6 +476,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))) { @@ -482,7 +502,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; } } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java index 683f23e345fc3..865aea62ef324 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java @@ -80,8 +80,8 @@ public boolean isExpired() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeStringField("accessToken", this.accessToken); - jsonWriter.writeNumberField("expiresIn", this.expiresIn); + jsonWriter.writeStringField("access_token", this.accessToken); + jsonWriter.writeNumberField("expires_in", this.expiresIn); return jsonWriter.writeEndObject(); } @@ -105,9 +105,9 @@ public static AccessToken fromJson(JsonReader jsonReader) throws IOException { reader.nextToken(); - if ("accessToken".equals(fieldName)) { + if ("access_token".equals(fieldName)) { deserializedAccessToken.accessToken = reader.getString(); - } else if ("expiresIn".equals(fieldName)) { + } else if ("expires_in".equals(fieldName)) { deserializedAccessToken.expiresIn = reader.getLong(); } else { reader.skipChildren(); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/CertificateListResult.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/CertificateListResult.java index 135c5cd4554bd..14f869d414c13 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/CertificateListResult.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/CertificateListResult.java @@ -16,49 +16,56 @@ */ public class CertificateListResult implements JsonSerializable { /** - * Stores the value. + * Stores the list of certificates. */ private List value; /** - * Get the value. + * Stores the link to the next certificates page. + */ + private String nextLink; + + /** + * Get the list of certificates in this page. * - * @return the id. + * @return The list of certificates in this page. */ public List getValue() { return value; } /** - * Set the value. + * Set the list of certificates in this page. * - * @param value the value. + * @param value the list of certificates in this page. */ public void setValue(List value) { this.value = value; } /** - * Get the NextLint - * @return the nextLink + * Get the link to the next certificates page. + * + * @return The link to the next certificates page. */ public String getNextLink() { return nextLink; } /** - * Set the NextLink - * @param nextLink the nextLink + * Set the link to the next certificates page. + * + * @param nextLink The link to the next certificates page. */ public void setNextLink(String nextLink) { this.nextLink = nextLink; } - private String nextLink; - @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); + jsonWriter.writeArrayField("value", value, JsonWriter::writeJson); + jsonWriter.writeStringField("nextLink", nextLink); return jsonWriter.writeEndObject(); } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/SignResult.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/SignResult.java index 822e80eb3fb16..6b2f2f9eb1c00 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/SignResult.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/SignResult.java @@ -54,6 +54,8 @@ public void setValue(String value) { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); + jsonWriter.writeStringField("kid", kid); + jsonWriter.writeStringField("value", value); return jsonWriter.writeEndObject(); } @@ -80,8 +82,7 @@ public static SignResult fromJson(JsonReader jsonReader) throws IOException { if ("kid".equals(fieldName)) { deserializedSignResult.kid = reader.getString(); } else if ("value".equals(fieldName)) { - deserializedSignResult.value = - reader.getNullable(nonNullReader -> new Base64Url(nonNullReader.getString()).toString()); + deserializedSignResult.value = new Base64Url(reader.getString()).toString(); } else { reader.skipChildren(); } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/AccessTokenUtil.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/AccessTokenUtil.java index 75b74c3d0fc36..c0f8a873df601 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/AccessTokenUtil.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/AccessTokenUtil.java @@ -5,6 +5,7 @@ import com.azure.security.keyvault.jca.implementation.model.AccessToken; import org.apache.http.HttpResponse; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -16,7 +17,6 @@ import java.util.logging.Logger; import static com.azure.security.keyvault.jca.implementation.utils.HttpUtil.addTrailingSlashIfRequired; -import static java.util.logging.Level.FINER; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -107,8 +107,7 @@ public static AccessToken getAccessToken(String resource, String identity) { * @return The authorization token. */ public static AccessToken getAccessToken(String resource, String aadAuthenticationUrl, String tenantId, - String clientId, String clientSecret) { - + String clientId, String clientSecret) { LOGGER.entering("AccessTokenUtil", "getAccessToken", new Object[] { resource, tenantId, clientId, clientSecret }); LOGGER.info("Getting access token using client ID / client secret"); @@ -145,13 +144,13 @@ public static AccessToken getAccessToken(String resource, String aadAuthenticati if (body != null) { try { - result = (AccessToken) JsonConverterUtil.fromJson(AccessToken.class, body); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse access token response"); + result = JsonConverterUtil.fromJson(AccessToken.class, body); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse access token response.", e); } } - LOGGER.log(FINER, "Access token: {0}", result); + LOGGER.exiting("AccessTokenUtil", "getAccessToken", result); return result; } @@ -189,9 +188,9 @@ private static AccessToken getAccessTokenOnAppService(String resource, String cl if (body != null) { try { - result = (AccessToken) JsonConverterUtil.fromJson(AccessToken.class, body); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse access token response"); + result = JsonConverterUtil.fromJson(AccessToken.class, body); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse access token response.", e); } } @@ -234,9 +233,9 @@ private static AccessToken getAccessTokenOnOthers(String resource, String identi if (body != null) { try { - result = (AccessToken) JsonConverterUtil.fromJson(AccessToken.class, body); - } catch (Throwable t) { - LOGGER.log(WARNING, "Failed to parse access token response"); + result = JsonConverterUtil.fromJson(AccessToken.class, body); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to parse access token response.", e); } } @@ -302,6 +301,8 @@ public static String getLoginUri(String resourceUri, boolean disableChallengeRes * @return A challenge attributes map. */ private static Map extractChallengeAttributes(String authenticateHeader) { + LOGGER.entering("AccessTokenUtil", "extractChallengeAttributes", authenticateHeader); + if (!isBearerChallenge(authenticateHeader)) { return Collections.emptyMap(); } @@ -318,6 +319,8 @@ private static Map extractChallengeAttributes(String authenticat attributeMap.put(keyValue[0].replaceAll("\"", ""), keyValue[1].replaceAll("\"", "")); } + LOGGER.exiting("AccessTokenUtil", "extractChallengeAttributes", attributeMap); + return attributeMap; } @@ -342,6 +345,8 @@ private static boolean isBearerChallenge(String authenticateHeader) { * @return A boolean indicating if the resource URI is valid or not. */ private static boolean isChallengeResourceValid(String resource, String scope) { + LOGGER.entering("AccessTokenUtil", "isChallengeResourceValid", new Object[] { resource, scope }); + final URI resourceUri; try { @@ -358,8 +363,12 @@ private static boolean isChallengeResourceValid(String resource, String scope) { throw new IllegalStateException("The challenge scope " + scope + " is not a valid URI.", e); } - // Returns false if the host specified in the scope does not match the requested domain. - return resourceUri.getHost().toLowerCase(Locale.ROOT) + boolean isValid = resourceUri.getHost().toLowerCase(Locale.ROOT) .endsWith("." + scopeUri.getHost().toLowerCase(Locale.ROOT)); + + LOGGER.exiting("AccessTokenUtil", "isChallengeResourceValid", isValid); + + // Returns false if the host specified in the scope does not match the requested domain. + return isValid; } } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/JsonConverterUtil.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/JsonConverterUtil.java index a34d3f93be2a8..63caf5a3ae045 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/JsonConverterUtil.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/JsonConverterUtil.java @@ -10,9 +10,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.lang.reflect.InvocationTargetException; import java.util.logging.Logger; import static java.util.logging.Level.WARNING; @@ -26,15 +24,6 @@ public final class JsonConverterUtil { */ private static final Logger LOGGER = Logger.getLogger(JsonConverterUtil.class.getName()); - /** - * Stores the JSON cache. - */ - private static final Map, ReflectiveInvoker> FROM_JSON_CACHE; - - static { - FROM_JSON_CACHE = new ConcurrentHashMap<>(); - } - /** * Deserializes the {@code json} as an instance of {@link JsonSerializable}. * @@ -47,45 +36,45 @@ public final class JsonConverterUtil { * @throws IllegalStateException If the {@code jsonSerializable} does not have a static {@code fromJson} method. * @throws Error If an error occurs during deserialization. */ - public static Object fromJson(Class jsonSerializable, String json) throws IOException { - if (FROM_JSON_CACHE.size() >= 10000) { - FROM_JSON_CACHE.clear(); - } + @SuppressWarnings("unchecked") + public static > T fromJson(Class jsonSerializable, String json) + throws IOException { - ReflectiveInvoker readJson = FROM_JSON_CACHE.computeIfAbsent(jsonSerializable, clazz -> { - try { - return ReflectionUtils.getMethodInvoker(clazz, - jsonSerializable.getDeclaredMethod("fromJson", JsonReader.class)); - } catch (Exception e) { - throw new IllegalStateException("Unable to find fromJson method", e); - } - }); + LOGGER.entering("JsonConverterUtil", "fromJson", new Object[] { jsonSerializable, json }); try (JsonReader jsonReader = JsonProviders.createReader(json)) { - return readJson.invokeStatic(jsonReader); - } catch (Throwable e) { - if (e instanceof IOException) { - throw (IOException) e; - } else if (e instanceof Exception) { - throw new IOException(e); - } else { - throw (Error) e; - } + T deserialized = (T) jsonSerializable.getMethod("fromJson", JsonReader.class).invoke(null, jsonReader); + + LOGGER.exiting("JsonConverterUtil", "fromJson", deserialized); + + return deserialized; + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + LOGGER.throwing("JsonConverterUtil", "fromJson", e); + + throw new RuntimeException(e); } } /** * Serializes an object to a JSON string. * - * @param object The object to serialize. + * @param jsonSerializable The object to serialize. */ - public static String toJson(JsonSerializable object) { - LOGGER.entering("JsonConverterUtil", "toJson", object); + @SuppressWarnings("CharsetObjectCanBeUsed") + public static String toJson(JsonSerializable jsonSerializable) { + LOGGER.entering("JsonConverterUtil", "toJson", jsonSerializable); + + if (jsonSerializable == null) { + return null; + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JsonWriter jsonWriter = JsonProviders.createWriter(byteArrayOutputStream)) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - object.toJson(JsonProviders.createWriter(outputStream)).flush(); + jsonWriter.writeUntyped(jsonSerializable); + jsonWriter.flush(); - return outputStream.toString(StandardCharsets.UTF_8); + return byteArrayOutputStream.toString("UTF-8"); } catch (IOException e) { LOGGER.log(WARNING, "Unable to convert to JSON", e); }