diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java b/auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java index e0b175e8..4ac74546 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java @@ -343,10 +343,9 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, privateKey, OAEP_SPEC); return cipher.doFinal(encryptedInput); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException e) { /* - * This exceptions are safe to be ignored: - * * - NoSuchPaddingException: * Thrown if PKCS1Padding is not available. Was introduced in API 1. * - NoSuchAlgorithmException: @@ -361,6 +360,24 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry */ Log.e(TAG, "The device can't decrypt input using a RSA Key.", e); throw new IncompatibleDeviceException(e); + } catch (ProviderException e) { + /* + * - ProviderException: + * Thrown on Android 12+ (API 31+, Keystore2) when the RSA key's padding + * restriction does not match the cipher transformation. For example, an RSA + * key generated with ENCRYPTION_PADDING_RSA_PKCS1 will trigger this when + * initialised with an OAEPWithSHA-1AndMGF1Padding cipher. On API 23-30 the + * same condition surfaces as InvalidKeyException. + * + * This is NOT a device-level incompatibility -- the key can be deleted and + * regenerated with the correct padding. Wrapping as CryptoException (rather + * than IncompatibleDeviceException) ensures the caller falls through to key + * cleanup and regeneration instead of permanently blocking the user. + */ + Log.e(TAG, "RSA key padding mismatch detected (Android 12+ Keystore2).", e); + deleteAESKeys(); + throw new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", e); } catch (IllegalArgumentException | IllegalBlockSizeException | BadPaddingException e) { /* * 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 Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey(), OAEP_SPEC); return cipher.doFinal(decryptedInput); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException e) { /* - * This exceptions are safe to be ignored: - * * - NoSuchPaddingException: * Thrown if PKCS1Padding is not available. Was introduced in API 1. * - NoSuchAlgorithmException: @@ -412,6 +428,24 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry */ Log.e(TAG, "The device can't encrypt input using a RSA Key.", e); throw new IncompatibleDeviceException(e); + } catch (ProviderException e) { + /* + * - ProviderException: + * Thrown on Android 12+ (API 31+, Keystore2) when the RSA key's padding + * restriction does not match the cipher transformation. For example, an RSA + * key generated with ENCRYPTION_PADDING_RSA_PKCS1 will trigger this when + * initialised with an OAEPWithSHA-1AndMGF1Padding cipher. On API 23-30 the + * same condition surfaces as InvalidKeyException. + * + * This is NOT a device-level incompatibility -- the key can be deleted and + * regenerated with the correct padding. Wrapping as CryptoException (rather + * than IncompatibleDeviceException) ensures the caller falls through to key + * cleanup and regeneration instead of permanently blocking the user. + */ + Log.e(TAG, "RSA key padding mismatch detected (Android 12+ Keystore2).", e); + deleteAESKeys(); + throw new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", e); } catch (IllegalBlockSizeException | BadPaddingException e) { /* * They really should not be thrown at all since padding is requested in the transformation. @@ -467,10 +501,12 @@ private byte[] attemptPKCS1Migration(byte[] encryptedAESBytes) { } catch (BadPaddingException | IllegalBlockSizeException e) { Log.e(TAG, "PKCS1 decryption failed. Data may be corrupted.", e); - } catch (KeyStoreException | CertificateException | IOException | + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | UnrecoverableEntryException | NoSuchPaddingException | InvalidKeyException e) { Log.e(TAG, "Migration failed due to key access error.", e); + } catch (ProviderException e) { + Log.e(TAG, "PKCS1 migration failed: key padding incompatible (Android 12+ Keystore2).", e); } catch (CryptoException e) { Log.e(TAG, "Failed to re-encrypt AES key with OAEP.", e); } @@ -593,7 +629,9 @@ private byte[] tryMigrateLegacyAESKey() { KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry(); byte[] decryptedAESKey = RSADecryptLegacyPKCS1(encryptedOldAESBytes, rsaKeyEntry.getPrivateKey()); - + + deleteRSAKeys(); + // Re-encrypt with OAEP and store at new location byte[] encryptedAESWithOAEP = RSAEncrypt(decryptedAESKey); String newEncodedEncryptedAES = new String(Base64.encode(encryptedAESWithOAEP, Base64.DEFAULT), StandardCharsets.UTF_8); @@ -603,7 +641,8 @@ private byte[] tryMigrateLegacyAESKey() { Log.d(TAG, "Legacy AES key migrated successfully"); return decryptedAESKey; } catch (CryptoException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | - BadPaddingException | IllegalBlockSizeException | IllegalArgumentException e) { + BadPaddingException | IllegalBlockSizeException | IllegalArgumentException | + ProviderException e) { Log.e(TAG, "Could not migrate legacy AES key. Will generate new key.", e); deleteAESKeys(); return null; @@ -632,8 +671,11 @@ private byte[] generateNewAESKey() throws IncompatibleDeviceException, CryptoExc } catch (NoSuchAlgorithmException e) { Log.e(TAG, "AES algorithm not available.", e); throw new IncompatibleDeviceException(e); + } catch (IncompatibleDeviceException e) { + deleteRSAKeys(); + deleteAESKeys(); + throw e; } catch (CryptoException e) { - // Re-throw CryptoException and its subclasses (including IncompatibleDeviceException) throw e; } catch (Exception e) { Log.e(TAG, "Unexpected error while creating new AES key.", e); diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java index d9f84c91..7e53a896 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java @@ -16,6 +16,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; @@ -1941,4 +1942,311 @@ public void shouldGenerateNewKeyWhenMigrationFails() throws Exception { Mockito.verify(storage, times(1)).remove(KEY_ALIAS); Mockito.verify(storage, times(1)).remove(OLD_KEY_ALIAS); } + + @Test + public void shouldWrapProviderExceptionFromCipherInitInRSADecryptAsCryptoException() { + Assert.assertThrows("The RSA key's padding mode is incompatible with the current cipher.", + CryptoException.class, () -> { + PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); + doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); + PowerMockito.mockStatic(Cipher.class); + PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + doThrow(new ProviderException(new KeyStoreException("Incompatible padding mode"))) + .when(rsaOaepCipher).init(eq(Cipher.DECRYPT_MODE), eq(privateKey), + any(AlgorithmParameterSpec.class)); + + cryptoUtil.RSADecrypt(new byte[]{1, 2, 3}); + }); + } + + @Test + public void shouldWrapProviderExceptionFromCipherInitInRSAEncryptAsCryptoException() { + Assert.assertThrows("The RSA key's padding mode is incompatible with the current cipher.", + CryptoException.class, () -> { + PublicKey publicKey = PowerMockito.mock(PublicKey.class); + Certificate certificate = PowerMockito.mock(Certificate.class); + doReturn(publicKey).when(certificate).getPublicKey(); + KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + doReturn(certificate).when(privateKeyEntry).getCertificate(); + doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); + PowerMockito.mockStatic(Cipher.class); + PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + doThrow(new ProviderException(new KeyStoreException("Incompatible padding mode"))) + .when(rsaOaepCipher).init(eq(Cipher.ENCRYPT_MODE), eq(publicKey), + any(AlgorithmParameterSpec.class)); + + cryptoUtil.RSAEncrypt(new byte[]{1, 2, 3}); + }); + } + + @Test + public void shouldTriggerPKCS1MigrationWhenRSADecryptThrowsProviderException() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + byte[] encryptedAESPKCS1 = new byte[]{10, 11, 12, 13}; + byte[] aesKeyBytes = new byte[32]; + Arrays.fill(aesKeyBytes, (byte) 0xAB); + byte[] reEncryptedOAEP = new byte[]{20, 21, 22, 23}; + String encodedPKCS1 = "pkcs1_encoded"; + String encodedOAEP = "oaep_encoded"; + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(encodedPKCS1); + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESPKCS1); + PowerMockito.when(Base64.encode(reEncryptedOAEP, Base64.DEFAULT)) + .thenReturn(encodedOAEP.getBytes(StandardCharsets.UTF_8)); + + doThrow(new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", + new ProviderException(new KeyStoreException("Incompatible padding mode")))) + .when(cryptoUtil).RSADecrypt(encryptedAESPKCS1); + + when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + KeyStore.PrivateKeyEntry mockEntry = mock(KeyStore.PrivateKeyEntry.class); + when(mockEntry.getPrivateKey()).thenReturn(mock(PrivateKey.class)); + when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class))) + .thenReturn(mockEntry); + when(rsaPkcs1Cipher.doFinal(encryptedAESPKCS1)).thenReturn(aesKeyBytes); + doReturn(reEncryptedOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(aesKeyBytes)); + Mockito.verify(storage).store(KEY_ALIAS, encodedOAEP); + Mockito.verify(keyStore).deleteEntry(KEY_ALIAS); + } + + @Test + public void shouldDeleteOldRSAKeyBeforeReEncryptingInTryMigrateLegacyAESKey() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + byte[] aesKeyBytes = new byte[32]; + Arrays.fill(aesKeyBytes, (byte) 0xCD); + byte[] encryptedOldAES = new byte[]{1, 2, 3, 4}; + byte[] encryptedNewAES = new byte[]{4, 5, 6}; + String encodedOldAES = "old_pkcs1_encoded"; + String encodedNewAES = "new_oaep_encoded"; + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(encodedOldAES); + + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedOldAES, Base64.DEFAULT)).thenReturn(encryptedOldAES); + PowerMockito.when(Base64.encode(encryptedNewAES, Base64.DEFAULT)) + .thenReturn(encodedNewAES.getBytes(StandardCharsets.UTF_8)); + + KeyStore.PrivateKeyEntry mockEntry = mock(KeyStore.PrivateKeyEntry.class); + PrivateKey mockPrivateKey = mock(PrivateKey.class); + when(mockEntry.getPrivateKey()).thenReturn(mockPrivateKey); + doReturn(mockEntry).when(cryptoUtil).getRSAKeyEntry(); + + when(rsaPkcs1Cipher.doFinal(encryptedOldAES)).thenReturn(aesKeyBytes); + + doReturn(encryptedNewAES).when(cryptoUtil).RSAEncrypt(aesKeyBytes); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(aesKeyBytes)); + Mockito.verify(storage).store(KEY_ALIAS, encodedNewAES); + Mockito.verify(storage).remove(OLD_KEY_ALIAS); + + + InOrder inOrder = Mockito.inOrder(keyStore, cryptoUtil); + inOrder.verify(keyStore).deleteEntry(KEY_ALIAS); + inOrder.verify(keyStore).deleteEntry(OLD_KEY_ALIAS); + inOrder.verify(cryptoUtil).RSAEncrypt(aesKeyBytes); + } + + @Test + public void shouldDeleteStaleRSAKeyAndRethrowOnIncompatibleDeviceExceptionDuringGenerateNewAESKey() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + + byte[] newAESKey = new byte[32]; + Arrays.fill(newAESKey, (byte) 0xEF); + SecretKey mockSecret = mock(SecretKey.class); + when(mockSecret.getEncoded()).thenReturn(newAESKey); + when(keyGenerator.generateKey()).thenReturn(mockSecret); + + doThrow(new IncompatibleDeviceException( + new ProviderException(new KeyStoreException("Incompatible padding mode")))) + .when(cryptoUtil).RSAEncrypt(newAESKey); + + Assert.assertThrows(IncompatibleDeviceException.class, () -> cryptoUtil.getAESKey()); + + Mockito.verify(keyStore).deleteEntry(KEY_ALIAS); + Mockito.verify(keyStore).deleteEntry(OLD_KEY_ALIAS); + Mockito.verify(storage).remove(KEY_ALIAS); + Mockito.verify(storage).remove(OLD_KEY_ALIAS); + } + + @Test + public void shouldHandleProviderExceptionInAttemptPKCS1Migration() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + byte[] encryptedAESPKCS1 = new byte[]{10, 11, 12, 13}; + String encodedPKCS1 = "pkcs1_encoded"; + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(encodedPKCS1); + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESPKCS1); + + doThrow(new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", + new ProviderException(new KeyStoreException("Incompatible padding mode")))) + .when(cryptoUtil).RSADecrypt(encryptedAESPKCS1); + + when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + KeyStore.PrivateKeyEntry mockEntry = mock(KeyStore.PrivateKeyEntry.class); + when(mockEntry.getPrivateKey()).thenReturn(mock(PrivateKey.class)); + when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class))) + .thenReturn(mockEntry); + when(rsaPkcs1Cipher.doFinal(encryptedAESPKCS1)) + .thenThrow(new ProviderException(new KeyStoreException("Incompatible padding mode"))); + + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + + byte[] newAESKey = new byte[32]; + Arrays.fill(newAESKey, (byte) 0xDD); + SecretKey mockSecret = mock(SecretKey.class); + when(mockSecret.getEncoded()).thenReturn(newAESKey); + when(keyGenerator.generateKey()).thenReturn(mockSecret); + + byte[] encryptedNewKey = new byte[]{30, 31, 32, 33}; + doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); + String encodedNewKey = "new_key_encoded"; + PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(newAESKey)); + Mockito.verify(storage).store(KEY_ALIAS, encodedNewKey); + } + + + @Test + public void shouldHandleProviderExceptionInTryMigrateLegacyAESKey() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); + + String encodedOldAES = "old_legacy_key"; + byte[] encryptedOldAES = new byte[]{1, 2, 3, 4}; + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(encodedOldAES); + + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedOldAES, Base64.DEFAULT)).thenReturn(encryptedOldAES); + + KeyStore.PrivateKeyEntry mockEntry = mock(KeyStore.PrivateKeyEntry.class); + PrivateKey mockPrivateKey = mock(PrivateKey.class); + when(mockEntry.getPrivateKey()).thenReturn(mockPrivateKey); + doReturn(mockEntry).when(cryptoUtil).getRSAKeyEntry(); + + when(rsaPkcs1Cipher.doFinal(encryptedOldAES)) + .thenThrow(new ProviderException(new KeyStoreException("Incompatible padding mode"))); + + byte[] newAESKey = new byte[32]; + Arrays.fill(newAESKey, (byte) 0xEE); + SecretKey mockSecret = mock(SecretKey.class); + when(mockSecret.getEncoded()).thenReturn(newAESKey); + when(keyGenerator.generateKey()).thenReturn(mockSecret); + + byte[] encryptedNewKey = new byte[]{40, 41, 42, 43}; + doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); + String encodedNewKey = "new_generated_key"; + PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(newAESKey)); + Mockito.verify(storage).store(KEY_ALIAS, encodedNewKey); + } + + + @Test + public void shouldFallThroughToKeyRegenerationWhenMigrationFailsWithCryptoException() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + byte[] encryptedAESPKCS1 = new byte[]{10, 11, 12, 13}; + String encodedPKCS1 = "pkcs1_encoded"; + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(encodedPKCS1); + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESPKCS1); + + doThrow(new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", + new ProviderException(new KeyStoreException("Incompatible padding mode")))) + .when(cryptoUtil).RSADecrypt(encryptedAESPKCS1); + + when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + when(keyStore.containsAlias(OLD_KEY_ALIAS)).thenReturn(false); + + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + + byte[] newAESKey = new byte[32]; + Arrays.fill(newAESKey, (byte) 0xFF); + SecretKey mockSecret = mock(SecretKey.class); + when(mockSecret.getEncoded()).thenReturn(newAESKey); + when(keyGenerator.generateKey()).thenReturn(mockSecret); + + byte[] encryptedNewKey = new byte[]{50, 51, 52, 53}; + doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); + String encodedNewKey = "regenerated_key"; + PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(newAESKey)); + Mockito.verify(storage).store(KEY_ALIAS, encodedNewKey); + Mockito.verify(keyStore).deleteEntry(KEY_ALIAS); + Mockito.verify(keyStore).deleteEntry(OLD_KEY_ALIAS); + } + + @Test + public void shouldNotPropagateProviderExceptionAsIncompatibleDeviceException() throws Exception { + CryptoUtil cryptoUtil = newCryptoUtilSpy(); + + byte[] encryptedAESPKCS1 = new byte[]{10, 11, 12, 13}; + String encodedPKCS1 = "pkcs1_encoded"; + + when(storage.retrieveString(KEY_ALIAS)).thenReturn(encodedPKCS1); + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.decode(encodedPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESPKCS1); + + doThrow(new CryptoException( + "The RSA key's padding mode is incompatible with the current cipher.", + new ProviderException(new KeyStoreException("Incompatible padding mode")))) + .when(cryptoUtil).RSADecrypt(encryptedAESPKCS1); + + when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + when(keyStore.containsAlias(OLD_KEY_ALIAS)).thenReturn(false); + + when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + + byte[] newAESKey = new byte[32]; + Arrays.fill(newAESKey, (byte) 0xAA); + SecretKey mockSecret = mock(SecretKey.class); + when(mockSecret.getEncoded()).thenReturn(newAESKey); + when(keyGenerator.generateKey()).thenReturn(mockSecret); + + byte[] encryptedNewKey = new byte[]{60, 61, 62, 63}; + doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); + String encodedNewKey = "recovered_key"; + PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + + byte[] result = cryptoUtil.getAESKey(); + + assertThat(result, is(notNullValue())); + assertThat(result, is(newAESKey)); + + } }