77import android .os .CancellationSignal ;
88import android .security .keystore .KeyGenParameterSpec ;
99import android .security .keystore .KeyInfo ;
10- import java .security .InvalidKeyException ;
10+ import android .security .KeyPairGeneratorSpec ;
1111import android .security .keystore .KeyProperties ;
1212import android .support .annotation .NonNull ;
1313import android .util .Base64 ;
2222import com .facebook .react .bridge .WritableNativeMap ;
2323import com .facebook .react .modules .core .DeviceEventManagerModule ;
2424
25+ import java .math .BigInteger ;
2526import java .security .Key ;
27+ import java .security .KeyPairGenerator ;
2628import java .security .KeyStore ;
29+ import java .security .InvalidKeyException ;
30+ import java .security .PrivateKey ;
31+ import java .security .PublicKey ;
32+ import java .util .Calendar ;
2733import java .util .HashMap ;
2834import java .util .Map ;
2935
3440import javax .crypto .spec .GCMParameterSpec ;
3541import javax .crypto .spec .IvParameterSpec ;
3642
43+ import javax .security .auth .x500 .X500Principal ;
44+
3745import br .com .classapp .RNSensitiveInfo .utils .AppConstants ;
3846import br .com .classapp .RNSensitiveInfo .view .Fragments .FingerprintAuthenticationDialogFragment ;
3947import br .com .classapp .RNSensitiveInfo .view .Fragments .FingerprintUiHelper ;
@@ -50,7 +58,7 @@ public class RNSensitiveInfoModule extends ReactContextBaseJavaModule {
5058 KeyProperties .ENCRYPTION_PADDING_PKCS7 ;
5159
5260 private static final String AES_GCM = "AES/GCM/NoPadding" ;
53- private static final String AES_ECB = "AES /ECB/PKCS7Padding " ;
61+ private static final String RSA_ECB = "RSA /ECB/PKCS1Padding " ;
5462 private static final String DELIMITER = "]" ;
5563 private static final byte [] FIXED_IV = {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 , 1 };
5664 private static final String KEY_ALIAS = "MySharedPreferenceKeyAlias" ;
@@ -62,13 +70,28 @@ public class RNSensitiveInfoModule extends ReactContextBaseJavaModule {
6270
6371 public RNSensitiveInfoModule (ReactApplicationContext reactContext ) {
6472 super (reactContext );
73+
74+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .JELLY_BEAN_MR2 ) {
75+ Exception cause = new RuntimeException ("Keystore is not supported!" );
76+ throw new RuntimeException ("Android version is too low" , cause );
77+ }
78+
79+ try {
80+ mKeyStore = KeyStore .getInstance (ANDROID_KEYSTORE_PROVIDER );
81+ mKeyStore .load (null );
82+ } catch (Exception e ) {
83+ e .printStackTrace ();
84+ }
85+
86+ initKeyStore ();
87+
6588 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
6689 try {
6790 mFingerprintManager = (FingerprintManager ) reactContext .getSystemService (Context .FINGERPRINT_SERVICE );
91+ initFingerprintKeyStore ();
6892 } catch (Exception e ) {
6993 Log .d ("RNSensitiveInfo" , "Fingerprint not supported" );
7094 }
71- initKeyStore ();
7295 }
7396 }
7497
@@ -162,7 +185,7 @@ public void setItem(String key, String value, ReadableMap options, Promise pm) {
162185 putExtra (key , encrypt (value ), prefs (name ));
163186 pm .resolve (value );
164187 } catch (Exception e ) {
165- Log . d ( "RNSensitiveInfo" , e . getCause (). getMessage () );
188+ e . printStackTrace ( );
166189 pm .reject (e );
167190 }
168191 }
@@ -227,40 +250,61 @@ private void putExtra(String key, String value, SharedPreferences mSharedPrefere
227250 SharedPreferences .Editor editor = mSharedPreferences .edit ();
228251 editor .putString (key , value ).apply ();
229252 }
230-
253+
231254 /**
232- * Generates a new AES key and stores it under the { @code KEY_ALIAS_AES } in the
255+ * Generates a new AES key and stores it under the { @code KEY_ALIAS } in the
233256 * Android Keystore.
234257 */
235258 private void initKeyStore () {
236259 try {
237- mKeyStore = KeyStore .getInstance (ANDROID_KEYSTORE_PROVIDER );
238- mKeyStore .load (null );
239-
240- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M && !mKeyStore .containsAlias (KEY_ALIAS )) {
241- KeyGenerator keyGenerator = KeyGenerator .getInstance (KeyProperties .KEY_ALGORITHM_AES , ANDROID_KEYSTORE_PROVIDER );
242- keyGenerator .init (
243- new KeyGenParameterSpec .Builder (KEY_ALIAS ,
244- KeyProperties .PURPOSE_ENCRYPT | KeyProperties .PURPOSE_DECRYPT )
245- .setBlockModes (KeyProperties .BLOCK_MODE_GCM )
246- .setEncryptionPaddings (KeyProperties .ENCRYPTION_PADDING_NONE )
247- .setRandomizedEncryptionRequired (false )
248- .build ());
249- keyGenerator .generateKey ();
260+ if (!mKeyStore .containsAlias (KEY_ALIAS )) {
261+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
262+ KeyGenerator keyGenerator = KeyGenerator .getInstance (KeyProperties .KEY_ALGORITHM_RSA , ANDROID_KEYSTORE_PROVIDER );
263+ keyGenerator .init (
264+ new KeyGenParameterSpec .Builder (KEY_ALIAS ,
265+ KeyProperties .PURPOSE_ENCRYPT | KeyProperties .PURPOSE_DECRYPT )
266+ .setBlockModes (KeyProperties .BLOCK_MODE_GCM )
267+ .setEncryptionPaddings (KeyProperties .ENCRYPTION_PADDING_NONE )
268+ .setRandomizedEncryptionRequired (false )
269+ .build ());
270+ keyGenerator .generateKey ();
271+ } else {
272+ Calendar notBefore = Calendar .getInstance ();
273+ Calendar notAfter = Calendar .getInstance ();
274+ notAfter .add (Calendar .YEAR , 10 );
275+ KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec .Builder (getReactApplicationContext ())
276+ .setAlias (KEY_ALIAS )
277+ .setSubject (new X500Principal ("CN=" + KEY_ALIAS ))
278+ .setSerialNumber (BigInteger .valueOf (1337 ))
279+ .setStartDate (notBefore .getTime ())
280+ .setEndDate (notAfter .getTime ())
281+ .build ();
282+ KeyPairGenerator kpGenerator = KeyPairGenerator .getInstance ("RSA" , ANDROID_KEYSTORE_PROVIDER );
283+ kpGenerator .initialize (spec );
284+ kpGenerator .generateKeyPair ();
285+ }
250286 }
251-
287+ } catch (Exception e ) {
288+ e .printStackTrace ();
289+ }
290+ }
291+
292+ /**
293+ * Generates a new AES key and stores it under the { @code KEY_ALIAS_AES } in the
294+ * Android Keystore.
295+ */
296+ private void initFingerprintKeyStore () {
297+ try {
252298 // Check if a generated key exists under the KEY_ALIAS_AES .
253299 if (!mKeyStore .containsAlias (KEY_ALIAS_AES )) {
254300 prepareKey ();
255301 }
256302 } catch (Exception e ) {
303+ e .printStackTrace ();
257304 }
258305 }
259306
260307 private void prepareKey () throws Exception {
261- if (android .os .Build .VERSION .SDK_INT < android .os .Build .VERSION_CODES .M ) {
262- return ;
263- }
264308
265309 KeyGenerator keyGenerator = KeyGenerator .getInstance (
266310 KeyProperties .KEY_ALGORITHM_AES , ANDROID_KEYSTORE_PROVIDER );
@@ -497,17 +541,17 @@ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult re
497541 }
498542
499543 public String encrypt (String input ) throws Exception {
500-
501- Key secretKey = ((KeyStore .SecretKeyEntry ) mKeyStore .getEntry (KEY_ALIAS , null )).getSecretKey ();
502544 byte [] bytes = input .getBytes ();
503545
504546 Cipher c ;
505547 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
548+ Key secretKey = ((KeyStore .SecretKeyEntry ) mKeyStore .getEntry (KEY_ALIAS , null )).getSecretKey ();
506549 c = Cipher .getInstance (AES_GCM );
507550 c .init (Cipher .ENCRYPT_MODE , secretKey , new GCMParameterSpec (128 , FIXED_IV ));
508551 } else {
509- c = Cipher .getInstance (AES_ECB , "BC" );
510- c .init (Cipher .ENCRYPT_MODE , secretKey );
552+ PublicKey publicKey = ((KeyStore .PrivateKeyEntry )mKeyStore .getEntry (KEY_ALIAS , null )).getCertificate ().getPublicKey ();
553+ c = Cipher .getInstance (RSA_ECB );
554+ c .init (Cipher .ENCRYPT_MODE , publicKey );
511555 }
512556 byte [] encodedBytes = c .doFinal (bytes );
513557 String encryptedBase64Encoded = Base64 .encodeToString (encodedBytes , Base64 .DEFAULT );
@@ -522,14 +566,15 @@ public String decrypt(String encrypted) throws Exception {
522566 }
523567
524568 Cipher c ;
525- Key secretKey = ((KeyStore .SecretKeyEntry ) mKeyStore .getEntry (KEY_ALIAS , null )).getSecretKey ();
526569
527570 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
571+ Key secretKey = ((KeyStore .SecretKeyEntry ) mKeyStore .getEntry (KEY_ALIAS , null )).getSecretKey ();
528572 c = Cipher .getInstance (AES_GCM );
529573 c .init (Cipher .DECRYPT_MODE , secretKey , new GCMParameterSpec (128 , FIXED_IV ));
530574 } else {
531- c = Cipher .getInstance (AES_ECB , "BC" );
532- c .init (Cipher .DECRYPT_MODE , secretKey );
575+ PrivateKey privateKey = ((KeyStore .PrivateKeyEntry )mKeyStore .getEntry (KEY_ALIAS , null )).getPrivateKey ();
576+ c = Cipher .getInstance (RSA_ECB );
577+ c .init (Cipher .DECRYPT_MODE , privateKey );
533578 }
534579 byte [] decodedBytes = c .doFinal (Base64 .decode (encrypted , Base64 .DEFAULT ));
535580 return new String (decodedBytes );
0 commit comments