Skip to content

Commit 2541bf6

Browse files
authored
Replace Deprecated Keystore API for Android 28+, Fixes AB#3110184 (#2558)
Issue : https://portal.microsofticm.com/imp/v5/incidents/details/540463066/summary Keystore operation "unwrap" is failing on Pixel 5 Android 14 devices. It is still not clear why the operation would fail specifically on Pixel 5 devices but it could be some bug on google side which is fixed for all other devices through a patch. Updates for Pixel 5 are stopped hence it may not have the google fix. However, the getSpecForKeyStoreKey method used while wrapping the key was using a deprecated API (KeyPairGeneratorSpec). It was deprecated in Android 23. Updating it to latest one has somehow resolved the issue for 1 customer (Since there are no updates from other customers, we assumed that it is the fix). Exception : [YPC] 2024-11-14 17:34:29.29 [25795][917] ERROR [AndroidKeyStoreUtil:unwrap] [2024-11-14 12:04:29 - thread_id: 911, correlation_id: UNSET - Android 34] invalid_key java.security.InvalidKeyException: Keystore operation failed at android.security.keystore2.KeyStoreCryptoOperationUtils.getInvalidKeyException(KeyStoreCryptoOperationUtils.java:128) at android.security.keystore2.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:152) at android.security.keystore2.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:354) Fix : Removed the deprecated API KeyPairGeneratorSpec and using the new one KeyGenParameterSpec which lets us set the purpose as PURPOSE_WRAP_KEY Testing : Ran the pipeline to confirm if the instrumented and UI tests are running as expected https://identitydivision.visualstudio.com/Engineering/_build/results?buildId=1401690&view=ms.vss-test-web.build-test-results-tab&runId=4352544&resultId=100000&paneView=debug and https://identitydivision.visualstudio.com/Engineering/_build/results?buildId=1401664&view=logs&s=60296c01-192d-58d3-82b8-da4d468e44bd Fixes [AB#3110184](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3110184)
1 parent e77bcfc commit 2541bf6

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ vNext
55
- [PATCH] Translate MFA token error to UIRequiredException instead of ServiceException (#2538)
66
- [MINOR] Add Child Spans for Interactive Span (#2516)
77
- [MINOR] For MSAL CPP flows, match exact claims when deleting AT with intersecting scopes (#2548)
8+
- [MINOR] Replace Deprecated Keystore API for Android 28+ (#2558)
89

910
Version 18.2.2
1011
----------

common/src/main/java/com/microsoft/identity/common/crypto/AndroidWrappedKeyLoader.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import android.content.Context;
2727
import android.os.Build;
2828
import android.security.KeyPairGeneratorSpec;
29+
import android.security.keystore.KeyGenParameterSpec;
30+
import android.security.keystore.KeyProperties;
2931

3032
import androidx.annotation.RequiresApi;
3133

@@ -44,7 +46,9 @@
4446
import java.security.KeyStore;
4547
import java.security.spec.AlgorithmParameterSpec;
4648
import java.util.Calendar;
49+
import java.util.Date;
4750
import java.util.Locale;
51+
import java.util.concurrent.TimeUnit;
4852

4953
import javax.crypto.SecretKey;
5054
import javax.security.auth.x500.X500Principal;
@@ -269,12 +273,13 @@ public void deleteSecretKeyFromStorage() throws ClientException {
269273
/**
270274
* Generate a self-signed cert and derive an AlgorithmParameterSpec from that.
271275
* This is for the key to be generated in {@link KeyStore} via {@link KeyPairGenerator}
276+
* Note : This is now only for API level < 28
272277
*
273278
* @param context an Android {@link Context} object.
274279
* @return a {@link AlgorithmParameterSpec} for the keystore key (that we'll use to wrap the secret key).
275280
*/
276281
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
277-
private static AlgorithmParameterSpec getSpecForKeyStoreKey(@NonNull final Context context,
282+
private static AlgorithmParameterSpec getLegacySpecForKeyStoreKey(@NonNull final Context context,
278283
@NonNull final String alias) {
279284
// Generate a self-signed cert.
280285
final String certInfo = String.format(Locale.ROOT, "CN=%s, OU=%s",
@@ -295,6 +300,34 @@ private static AlgorithmParameterSpec getSpecForKeyStoreKey(@NonNull final Conte
295300
.build();
296301
}
297302

303+
/**
304+
* Generate a self-signed cert and derive an AlgorithmParameterSpec from that.
305+
* This is for the key to be generated in {@link KeyStore} via {@link KeyPairGenerator}
306+
*
307+
* @param context an Android {@link Context} object.
308+
* @return a {@link AlgorithmParameterSpec} for the keystore key (that we'll use to wrap the secret key).
309+
*/
310+
private static AlgorithmParameterSpec getSpecForKeyStoreKey(@NonNull final Context context, @NonNull final String alias) {
311+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
312+
return getLegacySpecForKeyStoreKey(context, alias);
313+
} else {
314+
final String certInfo = String.format(Locale.ROOT, "CN=%s, OU=%s",
315+
alias,
316+
context.getPackageName());
317+
final int certValidYears = 100;
318+
int purposes = KeyProperties.PURPOSE_WRAP_KEY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT;
319+
return new KeyGenParameterSpec.Builder(alias, purposes)
320+
.setCertificateSubject(new X500Principal(certInfo))
321+
.setCertificateSerialNumber(BigInteger.ONE)
322+
.setCertificateNotBefore(new Date())
323+
.setCertificateNotAfter(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365 * certValidYears)))
324+
.setKeySize(2048)
325+
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
326+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
327+
.build();
328+
}
329+
}
330+
298331
/**
299332
* Get the file that stores the wrapped key.
300333
*/

0 commit comments

Comments
 (0)