Skip to content

Commit f79f8f1

Browse files
committed
Fixing bug calling get credentials multiple times after migration
1 parent a401e51 commit f79f8f1

2 files changed

Lines changed: 36 additions & 11 deletions

File tree

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,8 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry
429429

430430
/**
431431
* Attempts to migrate legacy PKCS1-encrypted AES key to OAEP format.
432-
* This method tries to decrypt the AES key using legacy PKCS1 padding.
433-
* If successful, it deletes the old keys so fresh OAEP keys can be generated.
432+
* This method tries to decrypt the AES key using legacy PKCS1 padding,
433+
* then re-encrypts it with OAEP and stores it for future use.
434434
*
435435
* @param encryptedAESBytes the encrypted AES key bytes
436436
* @return the decrypted AES key if migration succeeds, or null if migration fails
@@ -455,9 +455,14 @@ private byte[] attemptPKCS1Migration(byte[] encryptedAESBytes) {
455455
}
456456

457457
Log.d(TAG, "PKCS1 migration successful - deleting old keys");
458+
458459
deleteRSAKeys();
459-
deleteAESKeys();
460460

461+
byte[] encryptedAESWithOAEP = RSAEncrypt(decryptedAESKey);
462+
String encodedEncryptedAES = new String(Base64.encode(encryptedAESWithOAEP, Base64.DEFAULT), StandardCharsets.UTF_8);
463+
storage.store(KEY_ALIAS, encodedEncryptedAES);
464+
465+
Log.d(TAG, "AES key re-encrypted with OAEP and stored");
461466
return decryptedAESKey;
462467

463468
} catch (BadPaddingException | IllegalBlockSizeException e) {
@@ -466,6 +471,8 @@ private byte[] attemptPKCS1Migration(byte[] encryptedAESBytes) {
466471
NoSuchAlgorithmException | UnrecoverableEntryException |
467472
NoSuchPaddingException | InvalidKeyException e) {
468473
Log.e(TAG, "Migration failed due to key access error.", e);
474+
} catch (CryptoException e) {
475+
Log.e(TAG, "Failed to re-encrypt AES key with OAEP.", e);
469476
}
470477
return null;
471478
}
@@ -599,15 +606,15 @@ private byte[] tryMigrateLegacyAESKey() {
599606
try {
600607
byte[] encryptedOldAESBytes = Base64.decode(encodedOldAES, Base64.DEFAULT);
601608
KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry();
602-
609+
603610
byte[] decryptedAESKey = RSADecryptLegacyPKCS1(encryptedOldAESBytes, rsaKeyEntry.getPrivateKey());
604611

605612
// Re-encrypt with OAEP and store at new location
606613
byte[] encryptedAESWithOAEP = RSAEncrypt(decryptedAESKey);
607614
String newEncodedEncryptedAES = new String(Base64.encode(encryptedAESWithOAEP, Base64.DEFAULT), StandardCharsets.UTF_8);
608615
storage.store(KEY_ALIAS, newEncodedEncryptedAES);
609616
storage.remove(OLD_KEY_ALIAS);
610-
617+
611618
Log.d(TAG, "Legacy AES key migrated successfully");
612619
return decryptedAESKey;
613620
} catch (Exception e) {

auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,12 +1715,16 @@ public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception {
17151715

17161716
byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
17171717
byte[] encryptedAESKeyPKCS1 = new byte[]{20, 21, 22, 23, 24};
1718+
byte[] encryptedAESKeyOAEP = new byte[]{30, 31, 32, 33, 34};
17181719
String encodedEncryptedAESPKCS1 = "pkcs1_encrypted_key";
1720+
String encodedEncryptedAESOAEP = "oaep_encrypted_key";
17191721

17201722
when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAESPKCS1);
17211723
when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null);
17221724
PowerMockito.mockStatic(Base64.class);
17231725
PowerMockito.when(Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1);
1726+
PowerMockito.when(Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT))
1727+
.thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8));
17241728

17251729
IncompatibleDeviceException incompatibleException = new IncompatibleDeviceException(
17261730
new KeyStoreException("Incompatible padding mode")
@@ -1730,24 +1734,27 @@ public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception {
17301734
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
17311735
KeyStore.PrivateKeyEntry mockKeyEntry = mock(KeyStore.PrivateKeyEntry.class);
17321736
PrivateKey mockPrivateKey = mock(PrivateKey.class);
1737+
Certificate mockCertificate = mock(Certificate.class);
1738+
PublicKey mockPublicKey = mock(PublicKey.class);
17331739
when(mockKeyEntry.getPrivateKey()).thenReturn(mockPrivateKey);
1740+
when(mockKeyEntry.getCertificate()).thenReturn(mockCertificate);
1741+
when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey);
17341742
when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class)))
17351743
.thenReturn(mockKeyEntry);
17361744

17371745
when(rsaPkcs1Cipher.doFinal(encryptedAESKeyPKCS1)).thenReturn(aesKeyBytes);
1746+
1747+
doReturn(encryptedAESKeyOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes);
17381748

17391749
byte[] result = cryptoUtil.getAESKey();
17401750

17411751
assertThat(result, is(aesKeyBytes));
17421752

1743-
// Verify PKCS1 cipher was used to decrypt the legacy key
17441753
Mockito.verify(rsaPkcs1Cipher).init(Cipher.DECRYPT_MODE, mockPrivateKey);
17451754
Mockito.verify(rsaPkcs1Cipher).doFinal(encryptedAESKeyPKCS1);
17461755

1747-
// Simplified migration: RSA and AES keys are deleted (not re-encrypted)
1748-
// Next operation will generate fresh OAEP keys
17491756
Mockito.verify(keyStore).deleteEntry(KEY_ALIAS);
1750-
Mockito.verify(storage).remove(KEY_ALIAS);
1757+
Mockito.verify(storage).store(KEY_ALIAS, encodedEncryptedAESOAEP);
17511758
}
17521759

17531760
@Test
@@ -1861,13 +1868,17 @@ public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception {
18611868

18621869
byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
18631870
byte[] encryptedAESKeyPKCS1 = new byte[]{20, 21, 22, 23, 24};
1871+
byte[] encryptedAESKeyOAEP = new byte[]{30, 31, 32, 33, 34};
18641872
String encodedEncryptedAESPKCS1 = "pkcs1_encrypted_key";
1873+
String encodedEncryptedAESOAEP = "oaep_encrypted_key";
18651874

18661875
// First retrieval - migration happens, returns decrypted key
18671876
when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAESPKCS1);
18681877
when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null);
18691878
PowerMockito.mockStatic(Base64.class);
18701879
PowerMockito.when(Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1);
1880+
PowerMockito.when(Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT))
1881+
.thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8));
18711882

18721883
IncompatibleDeviceException incompatibleException = new IncompatibleDeviceException(
18731884
new KeyStoreException("Incompatible padding mode")
@@ -1877,18 +1888,25 @@ public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception {
18771888
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true);
18781889
KeyStore.PrivateKeyEntry mockKeyEntry = mock(KeyStore.PrivateKeyEntry.class);
18791890
PrivateKey mockPrivateKey = mock(PrivateKey.class);
1891+
Certificate mockCertificate = mock(Certificate.class);
1892+
PublicKey mockPublicKey = mock(PublicKey.class);
18801893
when(mockKeyEntry.getPrivateKey()).thenReturn(mockPrivateKey);
1894+
when(mockKeyEntry.getCertificate()).thenReturn(mockCertificate);
1895+
when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey);
18811896
when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class)))
18821897
.thenReturn(mockKeyEntry);
18831898

18841899
when(rsaPkcs1Cipher.doFinal(encryptedAESKeyPKCS1)).thenReturn(aesKeyBytes);
1900+
1901+
// Mock RSAEncrypt for re-encrypting with OAEP after migration
1902+
doReturn(encryptedAESKeyOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes);
18851903

18861904
byte[] result1 = cryptoUtil.getAESKey();
18871905
assertThat(result1, is(aesKeyBytes));
18881906

1889-
// Simplified migration: keys are deleted, not re-stored
1907+
// Migration should delete old keys and store re-encrypted AES key
18901908
Mockito.verify(keyStore).deleteEntry(KEY_ALIAS);
1891-
Mockito.verify(storage).remove(KEY_ALIAS);
1909+
Mockito.verify(storage).store(KEY_ALIAS, encodedEncryptedAESOAEP);
18921910
}
18931911

18941912
@Test

0 commit comments

Comments
 (0)