Skip to content

Commit f6c4126

Browse files
authored
fix: Handle ProviderException in PKCS1→OAEP key migration to prevent saveCredentials() crash (#924)
2 parents 9506d95 + f249712 commit f6c4126

File tree

2 files changed

+360
-10
lines changed

2 files changed

+360
-10
lines changed

auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,9 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry
343343
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
344344
cipher.init(Cipher.DECRYPT_MODE, privateKey, OAEP_SPEC);
345345
return cipher.doFinal(encryptedInput);
346-
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
346+
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
347+
| InvalidAlgorithmParameterException e) {
347348
/*
348-
* This exceptions are safe to be ignored:
349-
*
350349
* - NoSuchPaddingException:
351350
* Thrown if PKCS1Padding is not available. Was introduced in API 1.
352351
* - NoSuchAlgorithmException:
@@ -361,6 +360,24 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry
361360
*/
362361
Log.e(TAG, "The device can't decrypt input using a RSA Key.", e);
363362
throw new IncompatibleDeviceException(e);
363+
} catch (ProviderException e) {
364+
/*
365+
* - ProviderException:
366+
* Thrown on Android 12+ (API 31+, Keystore2) when the RSA key's padding
367+
* restriction does not match the cipher transformation. For example, an RSA
368+
* key generated with ENCRYPTION_PADDING_RSA_PKCS1 will trigger this when
369+
* initialised with an OAEPWithSHA-1AndMGF1Padding cipher. On API 23-30 the
370+
* same condition surfaces as InvalidKeyException.
371+
*
372+
* This is NOT a device-level incompatibility -- the key can be deleted and
373+
* regenerated with the correct padding. Wrapping as CryptoException (rather
374+
* than IncompatibleDeviceException) ensures the caller falls through to key
375+
* cleanup and regeneration instead of permanently blocking the user.
376+
*/
377+
Log.e(TAG, "RSA key padding mismatch detected (Android 12+ Keystore2).", e);
378+
deleteAESKeys();
379+
throw new CryptoException(
380+
"The RSA key's padding mode is incompatible with the current cipher.", e);
364381
} catch (IllegalArgumentException | IllegalBlockSizeException | BadPaddingException e) {
365382
/*
366383
* Any of this exceptions mean the encrypted input is somehow corrupted and cannot be recovered.
@@ -394,10 +411,9 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry
394411
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
395412
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey(), OAEP_SPEC);
396413
return cipher.doFinal(decryptedInput);
397-
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
414+
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
415+
| InvalidAlgorithmParameterException e) {
398416
/*
399-
* This exceptions are safe to be ignored:
400-
*
401417
* - NoSuchPaddingException:
402418
* Thrown if PKCS1Padding is not available. Was introduced in API 1.
403419
* - NoSuchAlgorithmException:
@@ -412,6 +428,24 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry
412428
*/
413429
Log.e(TAG, "The device can't encrypt input using a RSA Key.", e);
414430
throw new IncompatibleDeviceException(e);
431+
} catch (ProviderException e) {
432+
/*
433+
* - ProviderException:
434+
* Thrown on Android 12+ (API 31+, Keystore2) when the RSA key's padding
435+
* restriction does not match the cipher transformation. For example, an RSA
436+
* key generated with ENCRYPTION_PADDING_RSA_PKCS1 will trigger this when
437+
* initialised with an OAEPWithSHA-1AndMGF1Padding cipher. On API 23-30 the
438+
* same condition surfaces as InvalidKeyException.
439+
*
440+
* This is NOT a device-level incompatibility -- the key can be deleted and
441+
* regenerated with the correct padding. Wrapping as CryptoException (rather
442+
* than IncompatibleDeviceException) ensures the caller falls through to key
443+
* cleanup and regeneration instead of permanently blocking the user.
444+
*/
445+
Log.e(TAG, "RSA key padding mismatch detected (Android 12+ Keystore2).", e);
446+
deleteAESKeys();
447+
throw new CryptoException(
448+
"The RSA key's padding mode is incompatible with the current cipher.", e);
415449
} catch (IllegalBlockSizeException | BadPaddingException e) {
416450
/*
417451
* They really should not be thrown at all since padding is requested in the transformation.
@@ -467,10 +501,12 @@ private byte[] attemptPKCS1Migration(byte[] encryptedAESBytes) {
467501

468502
} catch (BadPaddingException | IllegalBlockSizeException e) {
469503
Log.e(TAG, "PKCS1 decryption failed. Data may be corrupted.", e);
470-
} catch (KeyStoreException | CertificateException | IOException |
504+
} catch (KeyStoreException | CertificateException | IOException |
471505
NoSuchAlgorithmException | UnrecoverableEntryException |
472506
NoSuchPaddingException | InvalidKeyException e) {
473507
Log.e(TAG, "Migration failed due to key access error.", e);
508+
} catch (ProviderException e) {
509+
Log.e(TAG, "PKCS1 migration failed: key padding incompatible (Android 12+ Keystore2).", e);
474510
} catch (CryptoException e) {
475511
Log.e(TAG, "Failed to re-encrypt AES key with OAEP.", e);
476512
}
@@ -593,7 +629,9 @@ private byte[] tryMigrateLegacyAESKey() {
593629
KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry();
594630

595631
byte[] decryptedAESKey = RSADecryptLegacyPKCS1(encryptedOldAESBytes, rsaKeyEntry.getPrivateKey());
596-
632+
633+
deleteRSAKeys();
634+
597635
// Re-encrypt with OAEP and store at new location
598636
byte[] encryptedAESWithOAEP = RSAEncrypt(decryptedAESKey);
599637
String newEncodedEncryptedAES = new String(Base64.encode(encryptedAESWithOAEP, Base64.DEFAULT), StandardCharsets.UTF_8);
@@ -603,7 +641,8 @@ private byte[] tryMigrateLegacyAESKey() {
603641
Log.d(TAG, "Legacy AES key migrated successfully");
604642
return decryptedAESKey;
605643
} catch (CryptoException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException |
606-
BadPaddingException | IllegalBlockSizeException | IllegalArgumentException e) {
644+
BadPaddingException | IllegalBlockSizeException | IllegalArgumentException |
645+
ProviderException e) {
607646
Log.e(TAG, "Could not migrate legacy AES key. Will generate new key.", e);
608647
deleteAESKeys();
609648
return null;
@@ -632,8 +671,11 @@ private byte[] generateNewAESKey() throws IncompatibleDeviceException, CryptoExc
632671
} catch (NoSuchAlgorithmException e) {
633672
Log.e(TAG, "AES algorithm not available.", e);
634673
throw new IncompatibleDeviceException(e);
674+
} catch (IncompatibleDeviceException e) {
675+
deleteRSAKeys();
676+
deleteAESKeys();
677+
throw e;
635678
} catch (CryptoException e) {
636-
// Re-throw CryptoException and its subclasses (including IncompatibleDeviceException)
637679
throw e;
638680
} catch (Exception e) {
639681
Log.e(TAG, "Unexpected error while creating new AES key.", e);

0 commit comments

Comments
 (0)