@@ -1998,7 +1998,8 @@ public void shouldTriggerPKCS1MigrationWhenRSADecryptThrowsProviderException() t
19981998 PowerMockito .when (Base64 .encode (reEncryptedOAEP , Base64 .DEFAULT ))
19991999 .thenReturn (encodedOAEP .getBytes (StandardCharsets .UTF_8 ));
20002000
2001- doThrow (new IncompatibleDeviceException (
2001+ doThrow (new CryptoException (
2002+ "The RSA key's padding mode is incompatible with the current cipher." ,
20022003 new ProviderException (new KeyStoreException ("Incompatible padding mode" ))))
20032004 .when (cryptoUtil ).RSADecrypt (encryptedAESPKCS1 );
20042005
@@ -2082,4 +2083,170 @@ public void shouldDeleteStaleRSAKeyAndRethrowOnIncompatibleDeviceExceptionDuring
20822083 Mockito .verify (storage ).remove (KEY_ALIAS );
20832084 Mockito .verify (storage ).remove (OLD_KEY_ALIAS );
20842085 }
2086+
2087+ @ Test
2088+ public void shouldHandleProviderExceptionInAttemptPKCS1Migration () throws Exception {
2089+ CryptoUtil cryptoUtil = newCryptoUtilSpy ();
2090+
2091+ byte [] encryptedAESPKCS1 = new byte []{10 , 11 , 12 , 13 };
2092+ String encodedPKCS1 = "pkcs1_encoded" ;
2093+
2094+ when (storage .retrieveString (KEY_ALIAS )).thenReturn (encodedPKCS1 );
2095+ PowerMockito .mockStatic (Base64 .class );
2096+ PowerMockito .when (Base64 .decode (encodedPKCS1 , Base64 .DEFAULT )).thenReturn (encryptedAESPKCS1 );
2097+
2098+ doThrow (new CryptoException (
2099+ "The RSA key's padding mode is incompatible with the current cipher." ,
2100+ new ProviderException (new KeyStoreException ("Incompatible padding mode" ))))
2101+ .when (cryptoUtil ).RSADecrypt (encryptedAESPKCS1 );
2102+
2103+ when (keyStore .containsAlias (KEY_ALIAS )).thenReturn (true );
2104+ KeyStore .PrivateKeyEntry mockEntry = mock (KeyStore .PrivateKeyEntry .class );
2105+ when (mockEntry .getPrivateKey ()).thenReturn (mock (PrivateKey .class ));
2106+ when (keyStore .getEntry (eq (KEY_ALIAS ), nullable (KeyStore .ProtectionParameter .class )))
2107+ .thenReturn (mockEntry );
2108+ when (rsaPkcs1Cipher .doFinal (encryptedAESPKCS1 ))
2109+ .thenThrow (new ProviderException (new KeyStoreException ("Incompatible padding mode" )));
2110+
2111+ when (storage .retrieveString (OLD_KEY_ALIAS )).thenReturn (null );
2112+
2113+ byte [] newAESKey = new byte [32 ];
2114+ Arrays .fill (newAESKey , (byte ) 0xDD );
2115+ SecretKey mockSecret = mock (SecretKey .class );
2116+ when (mockSecret .getEncoded ()).thenReturn (newAESKey );
2117+ when (keyGenerator .generateKey ()).thenReturn (mockSecret );
2118+
2119+ byte [] encryptedNewKey = new byte []{30 , 31 , 32 , 33 };
2120+ doReturn (encryptedNewKey ).when (cryptoUtil ).RSAEncrypt (any (byte [].class ));
2121+ String encodedNewKey = "new_key_encoded" ;
2122+ PowerMockito .when (Base64 .encode (encryptedNewKey , Base64 .DEFAULT ))
2123+ .thenReturn (encodedNewKey .getBytes (StandardCharsets .UTF_8 ));
2124+
2125+ byte [] result = cryptoUtil .getAESKey ();
2126+
2127+ assertThat (result , is (newAESKey ));
2128+ Mockito .verify (storage ).store (KEY_ALIAS , encodedNewKey );
2129+ }
2130+
2131+
2132+ @ Test
2133+ public void shouldHandleProviderExceptionInTryMigrateLegacyAESKey () throws Exception {
2134+ CryptoUtil cryptoUtil = newCryptoUtilSpy ();
2135+
2136+ when (storage .retrieveString (KEY_ALIAS )).thenReturn (null );
2137+
2138+ String encodedOldAES = "old_legacy_key" ;
2139+ byte [] encryptedOldAES = new byte []{1 , 2 , 3 , 4 };
2140+ when (storage .retrieveString (OLD_KEY_ALIAS )).thenReturn (encodedOldAES );
2141+
2142+ PowerMockito .mockStatic (Base64 .class );
2143+ PowerMockito .when (Base64 .decode (encodedOldAES , Base64 .DEFAULT )).thenReturn (encryptedOldAES );
2144+
2145+ KeyStore .PrivateKeyEntry mockEntry = mock (KeyStore .PrivateKeyEntry .class );
2146+ PrivateKey mockPrivateKey = mock (PrivateKey .class );
2147+ when (mockEntry .getPrivateKey ()).thenReturn (mockPrivateKey );
2148+ doReturn (mockEntry ).when (cryptoUtil ).getRSAKeyEntry ();
2149+
2150+ when (rsaPkcs1Cipher .doFinal (encryptedOldAES ))
2151+ .thenThrow (new ProviderException (new KeyStoreException ("Incompatible padding mode" )));
2152+
2153+ byte [] newAESKey = new byte [32 ];
2154+ Arrays .fill (newAESKey , (byte ) 0xEE );
2155+ SecretKey mockSecret = mock (SecretKey .class );
2156+ when (mockSecret .getEncoded ()).thenReturn (newAESKey );
2157+ when (keyGenerator .generateKey ()).thenReturn (mockSecret );
2158+
2159+ byte [] encryptedNewKey = new byte []{40 , 41 , 42 , 43 };
2160+ doReturn (encryptedNewKey ).when (cryptoUtil ).RSAEncrypt (any (byte [].class ));
2161+ String encodedNewKey = "new_generated_key" ;
2162+ PowerMockito .when (Base64 .encode (encryptedNewKey , Base64 .DEFAULT ))
2163+ .thenReturn (encodedNewKey .getBytes (StandardCharsets .UTF_8 ));
2164+
2165+ byte [] result = cryptoUtil .getAESKey ();
2166+
2167+ assertThat (result , is (newAESKey ));
2168+ Mockito .verify (storage ).store (KEY_ALIAS , encodedNewKey );
2169+ }
2170+
2171+
2172+ @ Test
2173+ public void shouldFallThroughToKeyRegenerationWhenMigrationFailsWithCryptoException () throws Exception {
2174+ CryptoUtil cryptoUtil = newCryptoUtilSpy ();
2175+
2176+ byte [] encryptedAESPKCS1 = new byte []{10 , 11 , 12 , 13 };
2177+ String encodedPKCS1 = "pkcs1_encoded" ;
2178+
2179+ when (storage .retrieveString (KEY_ALIAS )).thenReturn (encodedPKCS1 );
2180+ PowerMockito .mockStatic (Base64 .class );
2181+ PowerMockito .when (Base64 .decode (encodedPKCS1 , Base64 .DEFAULT )).thenReturn (encryptedAESPKCS1 );
2182+
2183+ doThrow (new CryptoException (
2184+ "The RSA key's padding mode is incompatible with the current cipher." ,
2185+ new ProviderException (new KeyStoreException ("Incompatible padding mode" ))))
2186+ .when (cryptoUtil ).RSADecrypt (encryptedAESPKCS1 );
2187+
2188+ when (keyStore .containsAlias (KEY_ALIAS )).thenReturn (false );
2189+ when (keyStore .containsAlias (OLD_KEY_ALIAS )).thenReturn (false );
2190+
2191+ when (storage .retrieveString (OLD_KEY_ALIAS )).thenReturn (null );
2192+
2193+ byte [] newAESKey = new byte [32 ];
2194+ Arrays .fill (newAESKey , (byte ) 0xFF );
2195+ SecretKey mockSecret = mock (SecretKey .class );
2196+ when (mockSecret .getEncoded ()).thenReturn (newAESKey );
2197+ when (keyGenerator .generateKey ()).thenReturn (mockSecret );
2198+
2199+ byte [] encryptedNewKey = new byte []{50 , 51 , 52 , 53 };
2200+ doReturn (encryptedNewKey ).when (cryptoUtil ).RSAEncrypt (any (byte [].class ));
2201+ String encodedNewKey = "regenerated_key" ;
2202+ PowerMockito .when (Base64 .encode (encryptedNewKey , Base64 .DEFAULT ))
2203+ .thenReturn (encodedNewKey .getBytes (StandardCharsets .UTF_8 ));
2204+
2205+ byte [] result = cryptoUtil .getAESKey ();
2206+
2207+ assertThat (result , is (newAESKey ));
2208+ Mockito .verify (storage ).store (KEY_ALIAS , encodedNewKey );
2209+ Mockito .verify (keyStore ).deleteEntry (KEY_ALIAS );
2210+ Mockito .verify (keyStore ).deleteEntry (OLD_KEY_ALIAS );
2211+ }
2212+
2213+ @ Test
2214+ public void shouldNotPropagateProviderExceptionAsIncompatibleDeviceException () throws Exception {
2215+ CryptoUtil cryptoUtil = newCryptoUtilSpy ();
2216+
2217+ byte [] encryptedAESPKCS1 = new byte []{10 , 11 , 12 , 13 };
2218+ String encodedPKCS1 = "pkcs1_encoded" ;
2219+
2220+ when (storage .retrieveString (KEY_ALIAS )).thenReturn (encodedPKCS1 );
2221+ PowerMockito .mockStatic (Base64 .class );
2222+ PowerMockito .when (Base64 .decode (encodedPKCS1 , Base64 .DEFAULT )).thenReturn (encryptedAESPKCS1 );
2223+
2224+ doThrow (new CryptoException (
2225+ "The RSA key's padding mode is incompatible with the current cipher." ,
2226+ new ProviderException (new KeyStoreException ("Incompatible padding mode" ))))
2227+ .when (cryptoUtil ).RSADecrypt (encryptedAESPKCS1 );
2228+
2229+ when (keyStore .containsAlias (KEY_ALIAS )).thenReturn (false );
2230+ when (keyStore .containsAlias (OLD_KEY_ALIAS )).thenReturn (false );
2231+
2232+ when (storage .retrieveString (OLD_KEY_ALIAS )).thenReturn (null );
2233+
2234+ byte [] newAESKey = new byte [32 ];
2235+ Arrays .fill (newAESKey , (byte ) 0xAA );
2236+ SecretKey mockSecret = mock (SecretKey .class );
2237+ when (mockSecret .getEncoded ()).thenReturn (newAESKey );
2238+ when (keyGenerator .generateKey ()).thenReturn (mockSecret );
2239+
2240+ byte [] encryptedNewKey = new byte []{60 , 61 , 62 , 63 };
2241+ doReturn (encryptedNewKey ).when (cryptoUtil ).RSAEncrypt (any (byte [].class ));
2242+ String encodedNewKey = "recovered_key" ;
2243+ PowerMockito .when (Base64 .encode (encryptedNewKey , Base64 .DEFAULT ))
2244+ .thenReturn (encodedNewKey .getBytes (StandardCharsets .UTF_8 ));
2245+
2246+ byte [] result = cryptoUtil .getAESKey ();
2247+
2248+ assertThat (result , is (notNullValue ()));
2249+ assertThat (result , is (newAESKey ));
2250+
2251+ }
20852252}
0 commit comments