From 7cc29d7d296abd8847e14562eb6ef226a1c58cd2 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Fri, 29 Aug 2025 20:06:24 -0400 Subject: [PATCH 01/13] add one more field to count --- .../internal/util/AndroidKeyStoreUtil.java | 22 ++++++++++++++++++- .../java/opentelemetry/AttributeName.java | 5 +++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 787f03ac8e..624916890f 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -87,7 +87,7 @@ public class AndroidKeyStoreUtil { private AndroidKeyStoreUtil() { } - private static final LongCounter sFailedAndroidKeyStoreUnwrapOperationCount = OTelUtility.createLongCounter( + public static final LongCounter sFailedAndroidKeyStoreUnwrapOperationCount = OTelUtility.createLongCounter( "failed_keystore_key_unwrap_operation_count", "Number of failed Android KeyStore unwrap operations" ); @@ -447,11 +447,17 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob exception ); if (exception instanceof InvalidKeyException) { + final KeyStoreException keyStoreException = findKeyStoreException(exception); + String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Excerption Found"; + if (ksMessage == null) { + ksMessage = "null"; + } final Attributes attributes = Attributes.builder() .put(AttributeName.keystore_operation.name(), "unwrap") .put(AttributeName.error_code.name(), errCode) .put(AttributeName.error_type.name(), clientException.getClass().getSimpleName()) .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(clientException)) + .put(AttributeName.keystore_exception_message.name(), ksMessage) .build(); sFailedAndroidKeyStoreUnwrapOperationCount.add(1, attributes); } @@ -464,4 +470,18 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob throw clientException; } + + private static @Nullable KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + while (throwable != null) { + if (throwable instanceof android.security.KeyStoreException) { + return (KeyStoreException) throwable; + } + throwable = throwable.getCause(); + } + return null; + } else { + return null; + } + } } diff --git a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java index 145c47efad..576171ea85 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java +++ b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java @@ -305,6 +305,11 @@ public enum AttributeName { */ keystore_exception_stack_trace, + /** + * Indicates the exception message from a Android KeyStore operation exception. + */ + keystore_exception_message, + /** * Indicates the new nonce found in the eSTS request. */ From 0deb882cb17afadd74013f9952502fdd896e3634 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Fri, 29 Aug 2025 20:11:40 -0400 Subject: [PATCH 02/13] add one more field to count --- .../internal/util/AndroidKeyStoreUtil.java | 17 ++++++++++++++++- .../java/opentelemetry/AttributeName.java | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 624916890f..1dfbd32c78 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -47,6 +47,8 @@ import java.security.cert.CertificateException; import java.security.spec.AlgorithmParameterSpec; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -450,7 +452,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob final KeyStoreException keyStoreException = findKeyStoreException(exception); String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Excerption Found"; if (ksMessage == null) { - ksMessage = "null"; + ksMessage = ""; } final Attributes attributes = Attributes.builder() .put(AttributeName.keystore_operation.name(), "unwrap") @@ -458,6 +460,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob .put(AttributeName.error_type.name(), clientException.getClass().getSimpleName()) .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(clientException)) .put(AttributeName.keystore_exception_message.name(), ksMessage) + .put(AttributeName.keystore_internal_error_code.name(), extractInternalKeystoreCode(ksMessage)) .build(); sFailedAndroidKeyStoreUnwrapOperationCount.add(1, attributes); } @@ -484,4 +487,16 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob return null; } } + + /** + * Use Regex to pull out the internal error code from the key store exception message + * @param message the exception message + * @return the internal error code, or "N/A" if it can't be found + */ + private static String extractInternalKeystoreCode(final String message) { + if (message == null) return ""; + Pattern pattern = Pattern.compile("internal Keystore code:\\s*(-?\\d+)"); + Matcher matcher = pattern.matcher(message); + return matcher.find() ? matcher.group(1) : ""; + } } diff --git a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java index 576171ea85..ba3cfb200e 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java +++ b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java @@ -310,6 +310,11 @@ public enum AttributeName { */ keystore_exception_message, + /** + * Indicates the error code from a Android KeyStore operation exception. + */ + keystore_internal_error_code, + /** * Indicates the new nonce found in the eSTS request. */ From 10930a058889257111227ffb4f9f39f688d474ab Mon Sep 17 00:00:00 2001 From: fadidurah Date: Fri, 29 Aug 2025 20:12:50 -0400 Subject: [PATCH 03/13] add one more field to count --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 1dfbd32c78..df960c34d6 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -89,7 +89,7 @@ public class AndroidKeyStoreUtil { private AndroidKeyStoreUtil() { } - public static final LongCounter sFailedAndroidKeyStoreUnwrapOperationCount = OTelUtility.createLongCounter( + private static final LongCounter sFailedAndroidKeyStoreUnwrapOperationCount = OTelUtility.createLongCounter( "failed_keystore_key_unwrap_operation_count", "Number of failed Android KeyStore unwrap operations" ); From a0f950c245147c89b27191c1da3af670c566957c Mon Sep 17 00:00:00 2001 From: fadidurah <88730756+fadidurah@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:11:39 -0400 Subject: [PATCH 04/13] Update common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index df960c34d6..15b4be22ae 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -450,7 +450,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob ); if (exception instanceof InvalidKeyException) { final KeyStoreException keyStoreException = findKeyStoreException(exception); - String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Excerption Found"; + String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Exception Found"; if (ksMessage == null) { ksMessage = ""; } From 27e8079f6dd86b7e06bf96adb8f76113323ff9f3 Mon Sep 17 00:00:00 2001 From: fadidurah <88730756+fadidurah@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:13:01 -0400 Subject: [PATCH 05/13] Update common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 15b4be22ae..14f9635178 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -474,11 +474,11 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob } - private static @Nullable KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { + private static @Nullable android.security.KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { while (throwable != null) { if (throwable instanceof android.security.KeyStoreException) { - return (KeyStoreException) throwable; + return (android.security.KeyStoreException) throwable; } throwable = throwable.getCause(); } From db8aaae49779f4bfd417d03b20816b4ba1e479bc Mon Sep 17 00:00:00 2001 From: fadidurah Date: Tue, 2 Sep 2025 11:18:02 -0400 Subject: [PATCH 06/13] type cast --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 14f9635178..1e9d83ac49 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -449,7 +449,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob exception ); if (exception instanceof InvalidKeyException) { - final KeyStoreException keyStoreException = findKeyStoreException(exception); + final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Exception Found"; if (ksMessage == null) { ksMessage = ""; From 1b7305b75b87d6963adc7b2e7e07e31fa40a3471 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 3 Sep 2025 01:18:23 -0400 Subject: [PATCH 07/13] adjustments --- .../internal/util/AndroidKeyStoreUtil.java | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 1e9d83ac49..255916325a 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -450,17 +450,26 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob ); if (exception instanceof InvalidKeyException) { final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); - String ksMessage = keyStoreException != null ? keyStoreException.getMessage() : "No Keystore Exception Found"; - if (ksMessage == null) { - ksMessage = ""; + String ksMessage; + final String ksNumericErrorCode; + if (keyStoreException != null) { + ksMessage = keyStoreException.getMessage(); + if (ksMessage == null) { + ksMessage = ""; + } + ksNumericErrorCode = getNumericErrorCodeFromKeyStoreException(keyStoreException); + } else { + ksMessage = "No Keystore Exception Found"; + ksNumericErrorCode = ""; } + final Attributes attributes = Attributes.builder() .put(AttributeName.keystore_operation.name(), "unwrap") .put(AttributeName.error_code.name(), errCode) .put(AttributeName.error_type.name(), clientException.getClass().getSimpleName()) .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(clientException)) .put(AttributeName.keystore_exception_message.name(), ksMessage) - .put(AttributeName.keystore_internal_error_code.name(), extractInternalKeystoreCode(ksMessage)) + .put(AttributeName.keystore_internal_error_code.name(), ksNumericErrorCode) .build(); sFailedAndroidKeyStoreUnwrapOperationCount.add(1, attributes); } @@ -473,7 +482,13 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob throw clientException; } - + /** + * Searches the causal chain of the given throwable for an instance of + * {@link android.security.KeyStoreException}. + * + * @param throwable The throwable to search. + * @return The found KeyStoreException, or null if none was found or the API level is below 33. + */ private static @Nullable android.security.KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { while (throwable != null) { @@ -489,14 +504,16 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob } /** - * Use Regex to pull out the internal error code from the key store exception message - * @param message the exception message - * @return the internal error code, or "N/A" if it can't be found + * Extracts the numeric error code from a KeyStoreException if available. + * + * @param exception The KeyStoreException from which to extract the error code. + * @return The numeric error code as a String, or an empty string if not available. */ - private static String extractInternalKeystoreCode(final String message) { - if (message == null) return ""; - Pattern pattern = Pattern.compile("internal Keystore code:\\s*(-?\\d+)"); - Matcher matcher = pattern.matcher(message); - return matcher.find() ? matcher.group(1) : ""; + private static String getNumericErrorCodeFromKeyStoreException(@NonNull android.security.KeyStoreException exception) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return String.valueOf(exception.getNumericErrorCode()); + } else { + return ""; + } } } From 4ba418070b04878924992c0405c52b00bed1cf47 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 3 Sep 2025 01:20:50 -0400 Subject: [PATCH 08/13] adjustments --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 255916325a..9d022fb36c 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -455,11 +455,11 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob if (keyStoreException != null) { ksMessage = keyStoreException.getMessage(); if (ksMessage == null) { - ksMessage = ""; + ksMessage = "Keystore exception found, no error message"; } ksNumericErrorCode = getNumericErrorCodeFromKeyStoreException(keyStoreException); } else { - ksMessage = "No Keystore Exception Found"; + ksMessage = "No keystore exception found"; ksNumericErrorCode = ""; } From 45c5dd0ea8a29bfceb502337c17a08fbcd973c04 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Tue, 9 Sep 2025 20:50:29 -0400 Subject: [PATCH 09/13] comments --- .../internal/util/AndroidKeyStoreUtil.java | 66 ++++++++++--------- .../java/opentelemetry/AttributeName.java | 2 +- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 9d022fb36c..07b72751ff 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -86,6 +86,11 @@ public class AndroidKeyStoreUtil { */ private static final String ANDROID_KEY_STORE_TYPE = "AndroidKeyStore"; + /** + * Max length of stack trace to search for a keystore exception + */ + private static int KEYSTORE_EXCEPTION_CAUSE_CHAIN_MAX_DEPTH = 20; + private AndroidKeyStoreUtil() { } @@ -449,27 +454,38 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob exception ); if (exception instanceof InvalidKeyException) { - final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); String ksMessage; + final String errorType; final String ksNumericErrorCode; - if (keyStoreException != null) { - ksMessage = keyStoreException.getMessage(); - if (ksMessage == null) { - ksMessage = "Keystore exception found, no error message"; + + // Check API Level before attempting to extract KeyStoreException details + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); + if (keyStoreException != null) { + ksMessage = keyStoreException.getMessage(); + if (ksMessage == null) { + ksMessage = "Keystore exception found, no error message"; + } + errorType = "KeyStoreException"; + ksNumericErrorCode = String.valueOf(keyStoreException.getNumericErrorCode()); + } else { + ksMessage = "No keystore exception found"; + errorType = "InvalidKeyException"; + ksNumericErrorCode = ""; } - ksNumericErrorCode = getNumericErrorCodeFromKeyStoreException(keyStoreException); } else { - ksMessage = "No keystore exception found"; + ksMessage = "API Level below 33, keystore exception not available"; + errorType = "InvalidKeyException"; ksNumericErrorCode = ""; } final Attributes attributes = Attributes.builder() .put(AttributeName.keystore_operation.name(), "unwrap") .put(AttributeName.error_code.name(), errCode) - .put(AttributeName.error_type.name(), clientException.getClass().getSimpleName()) - .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(clientException)) + .put(AttributeName.error_type.name(), errorType) + .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(exception)) .put(AttributeName.keystore_exception_message.name(), ksMessage) - .put(AttributeName.keystore_internal_error_code.name(), ksNumericErrorCode) + .put(AttributeName.keystore_numeric_error_code.name(), ksNumericErrorCode) .build(); sFailedAndroidKeyStoreUnwrapOperationCount.add(1, attributes); } @@ -490,30 +506,16 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob * @return The found KeyStoreException, or null if none was found or the API level is below 33. */ private static @Nullable android.security.KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - while (throwable != null) { - if (throwable instanceof android.security.KeyStoreException) { - return (android.security.KeyStoreException) throwable; - } - throwable = throwable.getCause(); + // Check up to a max depth to avoid infinite loops in case of circular references + int count = 0; + while (throwable != null && count < KEYSTORE_EXCEPTION_CAUSE_CHAIN_MAX_DEPTH) { + if (throwable instanceof android.security.KeyStoreException) { + return (android.security.KeyStoreException) throwable; } - return null; - } else { - return null; + throwable = throwable.getCause(); + count++; } - } - /** - * Extracts the numeric error code from a KeyStoreException if available. - * - * @param exception The KeyStoreException from which to extract the error code. - * @return The numeric error code as a String, or an empty string if not available. - */ - private static String getNumericErrorCodeFromKeyStoreException(@NonNull android.security.KeyStoreException exception) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - return String.valueOf(exception.getNumericErrorCode()); - } else { - return ""; - } + return null; } } diff --git a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java index ba3cfb200e..971ecef335 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java +++ b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java @@ -313,7 +313,7 @@ public enum AttributeName { /** * Indicates the error code from a Android KeyStore operation exception. */ - keystore_internal_error_code, + keystore_numeric_error_code, /** * Indicates the new nonce found in the eSTS request. From 6321d076f5a2c394c7331c89b166fb26ad5785c6 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 10 Sep 2025 14:33:16 -0400 Subject: [PATCH 10/13] ks-telem --- .../internal/util/AndroidKeyStoreUtil.java | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 07b72751ff..0a6ddc2034 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -57,6 +57,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.metrics.LongCounter; import lombok.NonNull; @@ -454,39 +455,11 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob exception ); if (exception instanceof InvalidKeyException) { - String ksMessage; - final String errorType; - final String ksNumericErrorCode; - - // Check API Level before attempting to extract KeyStoreException details - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); - if (keyStoreException != null) { - ksMessage = keyStoreException.getMessage(); - if (ksMessage == null) { - ksMessage = "Keystore exception found, no error message"; - } - errorType = "KeyStoreException"; - ksNumericErrorCode = String.valueOf(keyStoreException.getNumericErrorCode()); - } else { - ksMessage = "No keystore exception found"; - errorType = "InvalidKeyException"; - ksNumericErrorCode = ""; - } - } else { - ksMessage = "API Level below 33, keystore exception not available"; - errorType = "InvalidKeyException"; - ksNumericErrorCode = ""; - } - - final Attributes attributes = Attributes.builder() + final Attributes attributes = populateAttributesFromInvalidKeyException((InvalidKeyException) exception) .put(AttributeName.keystore_operation.name(), "unwrap") .put(AttributeName.error_code.name(), errCode) - .put(AttributeName.error_type.name(), errorType) - .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(exception)) - .put(AttributeName.keystore_exception_message.name(), ksMessage) - .put(AttributeName.keystore_numeric_error_code.name(), ksNumericErrorCode) .build(); + sFailedAndroidKeyStoreUnwrapOperationCount.add(1, attributes); } Logger.error( @@ -498,6 +471,43 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob throw clientException; } + /** + * Populate attributes from an InvalidKeyException, attempting to extract details from a nested + * KeyStoreException if available (API Level 33+). + */ + private static AttributesBuilder populateAttributesFromInvalidKeyException(final InvalidKeyException exception) { + String ksMessage; + final String errorType; + final String ksNumericErrorCode; + + // Check API Level before attempting to extract KeyStoreException details + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + final android.security.KeyStoreException keyStoreException = findKeyStoreException(exception); + if (keyStoreException != null) { + ksMessage = keyStoreException.getMessage(); + if (ksMessage == null) { + ksMessage = "Keystore exception found, no error message"; + } + errorType = "KeyStoreException"; + ksNumericErrorCode = String.valueOf(keyStoreException.getNumericErrorCode()); + } else { + ksMessage = "No keystore exception found"; + errorType = "InvalidKeyException"; + ksNumericErrorCode = ""; + } + } else { + ksMessage = "API Level below 33, keystore exception not available"; + errorType = "InvalidKeyException"; + ksNumericErrorCode = ""; + } + + return Attributes.builder() + .put(AttributeName.error_type.name(), errorType) + .put(AttributeName.keystore_exception_stack_trace.name(), ThrowableUtil.getStackTraceAsString(exception)) + .put(AttributeName.keystore_exception_message.name(), ksMessage) + .put(AttributeName.keystore_numeric_error_code.name(), ksNumericErrorCode); + } + /** * Searches the causal chain of the given throwable for an instance of * {@link android.security.KeyStoreException}. From c78d5fee3286ec66b1fafa30fd454b78a4101391 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 10 Sep 2025 14:34:47 -0400 Subject: [PATCH 11/13] ks-telem --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 0a6ddc2034..0cbc6c181e 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -455,7 +455,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob exception ); if (exception instanceof InvalidKeyException) { - final Attributes attributes = populateAttributesFromInvalidKeyException((InvalidKeyException) exception) + final Attributes attributes = createAttributesBuilderFromInvalidKeyException((InvalidKeyException) exception) .put(AttributeName.keystore_operation.name(), "unwrap") .put(AttributeName.error_code.name(), errCode) .build(); @@ -475,7 +475,7 @@ public static synchronized SecretKey unwrap(@NonNull final byte[] wrappedKeyBlob * Populate attributes from an InvalidKeyException, attempting to extract details from a nested * KeyStoreException if available (API Level 33+). */ - private static AttributesBuilder populateAttributesFromInvalidKeyException(final InvalidKeyException exception) { + private static AttributesBuilder createAttributesBuilderFromInvalidKeyException(final InvalidKeyException exception) { String ksMessage; final String errorType; final String ksNumericErrorCode; From a40806d512188ecbcc83633932af9c7827f1c856 Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 10 Sep 2025 14:35:50 -0400 Subject: [PATCH 12/13] ks-telem --- .../identity/common/internal/util/AndroidKeyStoreUtil.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index 0cbc6c181e..d708d75897 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -47,8 +47,6 @@ import java.security.cert.CertificateException; import java.security.spec.AlgorithmParameterSpec; import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; From 12363996d5947467bffdda3d9d66150c5d13c4af Mon Sep 17 00:00:00 2001 From: fadidurah Date: Wed, 10 Sep 2025 14:58:21 -0400 Subject: [PATCH 13/13] api check --- .../internal/util/AndroidKeyStoreUtil.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java index d708d75897..3408af705a 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/util/AndroidKeyStoreUtil.java @@ -514,16 +514,20 @@ private static AttributesBuilder createAttributesBuilderFromInvalidKeyException( * @return The found KeyStoreException, or null if none was found or the API level is below 33. */ private static @Nullable android.security.KeyStoreException findKeyStoreException(@NonNull Throwable throwable) { - // Check up to a max depth to avoid infinite loops in case of circular references - int count = 0; - while (throwable != null && count < KEYSTORE_EXCEPTION_CAUSE_CHAIN_MAX_DEPTH) { - if (throwable instanceof android.security.KeyStoreException) { - return (android.security.KeyStoreException) throwable; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Check up to a max depth to avoid infinite loops in case of circular references + int count = 0; + while (throwable != null && count < KEYSTORE_EXCEPTION_CAUSE_CHAIN_MAX_DEPTH) { + if (throwable instanceof android.security.KeyStoreException) { + return (android.security.KeyStoreException) throwable; + } + throwable = throwable.getCause(); + count++; } - throwable = throwable.getCause(); - count++; - } - return null; + return null; + } else { + return null; + } } }