diff --git a/mobile-app/android/app/src/main/java/com/aliasvault/Credential.java b/mobile-app/android/app/src/main/java/com/aliasvault/Credential.java deleted file mode 100644 index d94807c21..000000000 --- a/mobile-app/android/app/src/main/java/com/aliasvault/Credential.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.aliasvault; - -public class Credential { - private String username; - private String password; - private String service; - - public Credential(String username, String password, String service) { - this.username = username; - this.password = password; - this.service = service; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getService() { - return service; - } -} \ No newline at end of file diff --git a/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerModule.java b/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerModule.java deleted file mode 100644 index 7f760e093..000000000 --- a/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerModule.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.aliasvault; - -import android.util.Log; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; - -import java.util.List; - -public class CredentialManagerModule extends ReactContextBaseJavaModule { - private static final String TAG = "CredentialManagerModule"; - private final ReactApplicationContext reactContext; - - public CredentialManagerModule(ReactApplicationContext reactContext) { - super(reactContext); - this.reactContext = reactContext; - } - - @Override - public String getName() { - return "CredentialManager"; - } - - @ReactMethod - public void addCredential(String username, String password, String service, Promise promise) { - try { - SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); - Credential credential = new Credential(username, password, service); - store.addCredential(credential); - promise.resolve(true); - } catch (Exception e) { - Log.e(TAG, "Error adding credential", e); - promise.reject("ERR_ADD_CREDENTIAL", "Failed to add credential: " + e.getMessage(), e); - } - } - - @ReactMethod - public void getCredentials(Promise promise) { - try { - SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); - List credentials = store.getAllCredentials(); - - WritableArray credentialsArray = Arguments.createArray(); - for (Credential credential : credentials) { - WritableMap credentialMap = Arguments.createMap(); - credentialMap.putString("username", credential.getUsername()); - credentialMap.putString("password", credential.getPassword()); - credentialMap.putString("service", credential.getService()); - credentialsArray.pushMap(credentialMap); - } - - promise.resolve(credentialsArray); - } catch (Exception e) { - Log.e(TAG, "Error getting credentials", e); - promise.reject("ERR_GET_CREDENTIALS", "Failed to get credentials: " + e.getMessage(), e); - } - } - - @ReactMethod - public void clearCredentials(Promise promise) { - try { - SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); - store.clearAllCredentials(); - promise.resolve(true); - } catch (Exception e) { - Log.e(TAG, "Error clearing credentials", e); - promise.reject("ERR_CLEAR_CREDENTIALS", "Failed to clear credentials: " + e.getMessage(), e); - } - } -} \ No newline at end of file diff --git a/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerPackage.java b/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerPackage.java deleted file mode 100644 index 3e772a4ce..000000000 --- a/mobile-app/android/app/src/main/java/com/aliasvault/CredentialManagerPackage.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.aliasvault; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class CredentialManagerPackage implements ReactPackage { - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); - } - - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - List modules = new ArrayList<>(); - modules.add(new CredentialManagerModule(reactContext)); - return modules; - } -} \ No newline at end of file diff --git a/mobile-app/android/app/src/main/java/com/aliasvault/SharedCredentialStore.java b/mobile-app/android/app/src/main/java/com/aliasvault/SharedCredentialStore.java deleted file mode 100644 index a8dbad910..000000000 --- a/mobile-app/android/app/src/main/java/com/aliasvault/SharedCredentialStore.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.aliasvault; - -import android.content.Context; -import android.content.SharedPreferences; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; -import android.util.Base64; -import android.util.Log; - -import androidx.biometric.BiometricPrompt; -import androidx.fragment.app.FragmentActivity; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.UnrecoverableEntryException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.spec.GCMParameterSpec; - -public class SharedCredentialStore { - private static final String TAG = "SharedCredentialStore"; - private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; - private static final String ENCRYPTION_KEY_ALIAS = "aliasvault_encryption_key"; - private static final String SHARED_PREFS_NAME = "net.aliasvault.autofill"; - private static final String CREDENTIALS_KEY = "storedCredentials"; - private static final String IV_SUFFIX = "_iv"; - - private static SharedCredentialStore instance; - private final Context appContext; - private SecretKey cachedEncryptionKey; - - private SharedCredentialStore(Context context) { - this.appContext = context.getApplicationContext(); - } - - public static synchronized SharedCredentialStore getInstance(Context context) { - if (instance == null) { - instance = new SharedCredentialStore(context); - } - return instance; - } - - private SecretKey getOrCreateEncryptionKey() throws Exception { - if (cachedEncryptionKey != null) { - return cachedEncryptionKey; - } - - try { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); - keyStore.load(null); - - // Check if the key exists - if (keyStore.containsAlias(ENCRYPTION_KEY_ALIAS)) { - // Key exists, retrieve it - KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry( - ENCRYPTION_KEY_ALIAS, null); - cachedEncryptionKey = secretKeyEntry.getSecretKey(); - return cachedEncryptionKey; - } else { - // Key doesn't exist, create it - KeyGenerator keyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE); - - KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder( - ENCRYPTION_KEY_ALIAS, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .setUserAuthenticationRequired(true) - .build(); - - keyGenerator.init(keyGenParameterSpec); - cachedEncryptionKey = keyGenerator.generateKey(); - return cachedEncryptionKey; - } - } catch (Exception e) { - Log.e(TAG, "Error getting or creating encryption key", e); - throw e; - } - } - - private Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException { - return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_GCM + "/" - + KeyProperties.ENCRYPTION_PADDING_NONE); - } - - private String encrypt(String data) throws Exception { - SecretKey key = getOrCreateEncryptionKey(); - Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, key); - - byte[] iv = cipher.getIV(); - byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); - - // Store IV in SharedPreferences - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putString(CREDENTIALS_KEY + IV_SUFFIX, Base64.encodeToString(iv, Base64.DEFAULT)).apply(); - - return Base64.encodeToString(encryptedBytes, Base64.DEFAULT); - } - - private String decrypt(String encryptedData) throws Exception { - SecretKey key = getOrCreateEncryptionKey(); - Cipher cipher = getCipher(); - - // Get IV from SharedPreferences - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - String ivString = prefs.getString(CREDENTIALS_KEY + IV_SUFFIX, null); - if (ivString == null) { - throw new Exception("IV not found for decryption"); - } - - byte[] iv = Base64.decode(ivString, Base64.DEFAULT); - byte[] encryptedBytes = Base64.decode(encryptedData, Base64.DEFAULT); - - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv); - cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec); - - byte[] decryptedBytes = cipher.doFinal(encryptedBytes); - return new String(decryptedBytes, StandardCharsets.UTF_8); - } - - public List getAllCredentials() throws Exception { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - String encryptedData = prefs.getString(CREDENTIALS_KEY, null); - - if (encryptedData == null) { - return new ArrayList<>(); - } - - String decryptedData = decrypt(encryptedData); - return parseCredentialsFromJson(decryptedData); - } - - public void addCredential(Credential credential) throws Exception { - List credentials = getAllCredentials(); - credentials.add(credential); - - String jsonData = credentialsToJson(credentials); - String encryptedData = encrypt(jsonData); - - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putString(CREDENTIALS_KEY, encryptedData).apply(); - } - - public void clearAllCredentials() { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit() - .remove(CREDENTIALS_KEY) - .remove(CREDENTIALS_KEY + IV_SUFFIX) - .apply(); - - try { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); - keyStore.load(null); - if (keyStore.containsAlias(ENCRYPTION_KEY_ALIAS)) { - keyStore.deleteEntry(ENCRYPTION_KEY_ALIAS); - } - cachedEncryptionKey = null; - } catch (Exception e) { - Log.e(TAG, "Error clearing encryption key", e); - } - } - - public void clearCache() { - cachedEncryptionKey = null; - } - - private List parseCredentialsFromJson(String json) throws JSONException { - List credentials = new ArrayList<>(); - JSONArray jsonArray = new JSONArray(json); - - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - String username = jsonObject.getString("username"); - String password = jsonObject.getString("password"); - String service = jsonObject.getString("service"); - - credentials.add(new Credential(username, password, service)); - } - - return credentials; - } - - private String credentialsToJson(List credentials) throws JSONException { - JSONArray jsonArray = new JSONArray(); - - for (Credential credential : credentials) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("username", credential.getUsername()); - jsonObject.put("password", credential.getPassword()); - jsonObject.put("service", credential.getService()); - - jsonArray.put(jsonObject); - } - - return jsonArray.toString(); - } -} \ No newline at end of file diff --git a/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/CredentialManagerModule.java b/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/CredentialManagerModule.java index d95b83737..657757c91 100644 --- a/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/CredentialManagerModule.java +++ b/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/CredentialManagerModule.java @@ -57,7 +57,7 @@ public class CredentialManagerModule extends ReactContextBaseJavaModule { SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); Credential credential = new Credential(username, password, service); - store.addCredentialWithBiometricAuth(activity, credential, new SharedCredentialStore.CryptoOperationCallback() { + store.saveCredential(activity, credential, new SharedCredentialStore.CryptoOperationCallback() { @Override public void onSuccess(String result) { promise.resolve(true); @@ -91,7 +91,7 @@ public class CredentialManagerModule extends ReactContextBaseJavaModule { SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); - store.getAllCredentialsWithBiometricAuth(activity, new SharedCredentialStore.CryptoOperationCallback() { + store.getAllCredentials(activity, new SharedCredentialStore.CryptoOperationCallback() { @Override public void onSuccess(String jsonString) { try { @@ -132,7 +132,7 @@ public class CredentialManagerModule extends ReactContextBaseJavaModule { public void clearCredentials(final Promise promise) { try { SharedCredentialStore store = SharedCredentialStore.getInstance(reactContext); - store.clearAllCredentials(); + store.clearAllData(); promise.resolve(true); } catch (Exception e) { Log.e(TAG, "Error clearing credentials", e); diff --git a/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/SharedCredentialStore.java b/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/SharedCredentialStore.java index 6117c1e1a..137bfca4b 100644 --- a/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/SharedCredentialStore.java +++ b/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialmanager/SharedCredentialStore.java @@ -2,415 +2,129 @@ package net.aliasvault.app.credentialmanager; import android.content.Context; import android.content.SharedPreferences; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; -import android.util.Base64; import android.util.Log; -import androidx.biometric.BiometricPrompt; import androidx.fragment.app.FragmentActivity; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.nio.charset.StandardCharsets; -import java.security.KeyStore; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.spec.GCMParameterSpec; public class SharedCredentialStore { private static final String TAG = "SharedCredentialStore"; - private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; - private static final String ENCRYPTION_KEY_ALIAS = "aliasvault_encryption_key"; - private static final String SHARED_PREFS_NAME = "net.aliasvault.autofill"; - private static final String CREDENTIALS_KEY = "storedCredentials"; - private static final String IV_SUFFIX = "_iv"; - + private static final String SHARED_PREFS_NAME = "net.aliasvault.credentials"; + private static final String CREDENTIALS_KEY = "stored_credentials"; + private static SharedCredentialStore instance; private final Context appContext; - private SecretKey cachedEncryptionKey; - - // Interface for crypto operations that need biometric auth + + // Interface for operations that need callbacks public interface CryptoOperationCallback { void onSuccess(String result); void onError(Exception e); } - - // Interface for crypto key operations that need biometric auth - public interface KeyOperationCallback { - void onKeyReady(SecretKey key); - void onError(Exception e); - } - + private SharedCredentialStore(Context context) { this.appContext = context.getApplicationContext(); } - + public static synchronized SharedCredentialStore getInstance(Context context) { if (instance == null) { instance = new SharedCredentialStore(context); } return instance; } - - private SecretKey getOrCreateEncryptionKey() throws Exception { - if (cachedEncryptionKey != null) { - return cachedEncryptionKey; - } - - throw new Exception("Biometric authentication required. Use getEncryptionKeyWithBiometricAuth instead."); - } - - private Cipher getCipher() throws Exception { - return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_GCM + "/" - + KeyProperties.ENCRYPTION_PADDING_NONE); - } - + /** - * Authenticate user with biometric and get encryption key. - * This method will prompt for authentication even if key is already cached. + * Save a credential to SharedPreferences */ - public void getEncryptionKeyWithBiometricAuth(final FragmentActivity activity, final KeyOperationCallback callback) { + public void saveCredential(FragmentActivity activity, final Credential credential, + final CryptoOperationCallback callback) { try { - // First check if the key exists in the keystore - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); - keyStore.load(null); - - SecretKey keyToUse; - boolean createNewKey = false; - - if (keyStore.containsAlias(ENCRYPTION_KEY_ALIAS)) { - // Key exists, retrieve it but we'll need authentication to use it - KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry( - ENCRYPTION_KEY_ALIAS, null); - keyToUse = secretKeyEntry.getSecretKey(); - } else { - // Key doesn't exist, we'll create it after authentication - createNewKey = true; - keyToUse = null; - } - - // We'll use the cipher to verify biometric auth works - final Cipher cipher = getCipher(); - - if (!createNewKey) { - // If we have a key, initialize the cipher with it to verify auth - try { - // Get IV from preferences if available for decryption mode - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - String ivString = prefs.getString(CREDENTIALS_KEY + IV_SUFFIX, null); - - if (ivString != null) { - // We have IV, use decrypt mode - byte[] iv = Base64.decode(ivString, Base64.DEFAULT); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv); - cipher.init(Cipher.DECRYPT_MODE, keyToUse, gcmParameterSpec); - } else { - // No IV, use encrypt mode - cipher.init(Cipher.ENCRYPT_MODE, keyToUse); - } - } catch (Exception e) { - Log.e(TAG, "Error initializing cipher for auth check", e); - // If we fail to initialize (likely due to auth being required), - // we'll continue and let the biometric prompt handle it - } - } - - final boolean finalCreateNewKey = createNewKey; - final SecretKey finalKeyToUse = keyToUse; - - // Create biometric prompt - final Executor executor = Executors.newSingleThreadExecutor(); - final BiometricPrompt.AuthenticationCallback authCallback = new BiometricPrompt.AuthenticationCallback() { - @Override - public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { - try { - if (finalCreateNewKey) { - // Create a new key since it didn't exist - KeyGenerator keyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE); - - KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder( - ENCRYPTION_KEY_ALIAS, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .setUserAuthenticationRequired(true) - // This is critical: set validation timeout to prevent requiring auth for each operation - .setUserAuthenticationValidityDurationSeconds(30) - .build(); - - keyGenerator.init(keyGenParameterSpec); - cachedEncryptionKey = keyGenerator.generateKey(); - } else { - // Use existing key - cachedEncryptionKey = finalKeyToUse; - } - - callback.onKeyReady(cachedEncryptionKey); - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onAuthenticationError(int errorCode, CharSequence errString) { - callback.onError(new Exception("Authentication error: " + errString)); - } - - @Override - public void onAuthenticationFailed() { - callback.onError(new Exception("Authentication failed")); - } - }; - - // Show biometric prompt on main thread - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - try { - BiometricPrompt biometricPrompt = new BiometricPrompt(activity, executor, authCallback); - - // Show biometric prompt - BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() - .setTitle("Authenticate to Access Secure Storage") - .setSubtitle("Authentication is required to access your credentials") - .setNegativeButtonText("Cancel") - .build(); - - if (finalCreateNewKey) { - biometricPrompt.authenticate(promptInfo); - } else { - biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher)); - } - } catch (Exception e) { - callback.onError(e); - } - } - }); - - } catch (Exception e) { - callback.onError(e); - } - } - - public void encryptWithBiometricAuth(final FragmentActivity activity, final String data, final CryptoOperationCallback callback) { - getEncryptionKeyWithBiometricAuth(activity, new KeyOperationCallback() { - @Override - public void onKeyReady(SecretKey key) { - try { - final Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, key); - - // Perform encryption - byte[] iv = cipher.getIV(); - byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); - - // Store IV in SharedPreferences - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putString(CREDENTIALS_KEY + IV_SUFFIX, Base64.encodeToString(iv, Base64.DEFAULT)).apply(); - - callback.onSuccess(Base64.encodeToString(encryptedBytes, Base64.DEFAULT)); - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - - public void decryptWithBiometricAuth(final FragmentActivity activity, final String encryptedData, final CryptoOperationCallback callback) { - try { - // Get IV from SharedPreferences + Log.d(TAG, "Saving credential for: " + credential.getService()); + + // Get current credentials from SharedPreferences SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - String ivString = prefs.getString(CREDENTIALS_KEY + IV_SUFFIX, null); - if (ivString == null) { - callback.onError(new Exception("IV not found for decryption")); - return; - } - - final byte[] iv = Base64.decode(ivString, Base64.DEFAULT); - - getEncryptionKeyWithBiometricAuth(activity, new KeyOperationCallback() { - @Override - public void onKeyReady(SecretKey key) { - try { - Cipher cipher = getCipher(); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv); - cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec); - - // Perform decryption - byte[] encryptedBytes = Base64.decode(encryptedData, Base64.DEFAULT); - byte[] decryptedBytes = cipher.doFinal(encryptedBytes); - String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8); - - callback.onSuccess(decryptedData); - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); + String storedCredentialsJson = prefs.getString(CREDENTIALS_KEY, "[]"); + + // Parse existing credentials + List credentials = parseCredentialsFromJson(storedCredentialsJson); + + // Add new credential + credentials.add(credential); + + // Save updated credentials + String updatedJsonData = credentialsToJson(credentials); + prefs.edit().putString(CREDENTIALS_KEY, updatedJsonData).apply(); + + callback.onSuccess("Credential saved successfully"); } catch (Exception e) { callback.onError(e); } } - - public void getAllCredentialsWithBiometricAuth(final FragmentActivity activity, final CryptoOperationCallback callback) { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - final String encryptedData = prefs.getString(CREDENTIALS_KEY, null); - - if (encryptedData == null) { - try { - callback.onSuccess(new JSONArray().toString()); - } catch (Exception e) { - callback.onError(e); - } - return; - } - - decryptWithBiometricAuth(activity, encryptedData, new CryptoOperationCallback() { - @Override - public void onSuccess(String decryptedData) { - callback.onSuccess(decryptedData); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - - public void addCredentialWithBiometricAuth(final FragmentActivity activity, final Credential credential, final CryptoOperationCallback callback) { - getAllCredentialsWithBiometricAuth(activity, new CryptoOperationCallback() { - @Override - public void onSuccess(String result) { - try { - List credentials = parseCredentialsFromJson(result); - credentials.add(credential); - - String jsonData = credentialsToJson(credentials); - encryptWithBiometricAuth(activity, jsonData, new CryptoOperationCallback() { - @Override - public void onSuccess(String encryptedData) { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putString(CREDENTIALS_KEY, encryptedData).apply(); - callback.onSuccess("Credential added successfully"); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onError(Exception e) { - // If there's an error getting credentials (might be first use), create a new list - try { - List credentials = new ArrayList<>(); - credentials.add(credential); - - String jsonData = credentialsToJson(credentials); - encryptWithBiometricAuth(activity, jsonData, new CryptoOperationCallback() { - @Override - public void onSuccess(String encryptedData) { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putString(CREDENTIALS_KEY, encryptedData).apply(); - callback.onSuccess("Credential added successfully"); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } catch (Exception ex) { - callback.onError(ex); - } - } - }); - } - - public void clearAllCredentials() { - SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit() - .remove(CREDENTIALS_KEY) - .remove(CREDENTIALS_KEY + IV_SUFFIX) - .apply(); - + + /** + * Get all credentials from SharedPreferences + */ + public void getAllCredentials(FragmentActivity activity, final CryptoOperationCallback callback) { try { - KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); - keyStore.load(null); - if (keyStore.containsAlias(ENCRYPTION_KEY_ALIAS)) { - keyStore.deleteEntry(ENCRYPTION_KEY_ALIAS); - } - cachedEncryptionKey = null; + Log.d(TAG, "Retrieving credentials from SharedPreferences"); + + // Get credentials from SharedPreferences + SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); + String storedCredentialsJson = prefs.getString(CREDENTIALS_KEY, "[]"); + + callback.onSuccess(storedCredentialsJson); } catch (Exception e) { - Log.e(TAG, "Error clearing encryption key", e); + callback.onError(e); } } - - public void clearCache() { - cachedEncryptionKey = null; + + /** + * Clear all credentials from SharedPreferences + */ + public void clearAllData() { + Log.d(TAG, "Clearing all credentials from SharedPreferences"); + SharedPreferences prefs = appContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); + prefs.edit().remove(CREDENTIALS_KEY).apply(); } - + private List parseCredentialsFromJson(String json) throws JSONException { List credentials = new ArrayList<>(); - + if (json == null || json.isEmpty()) { return credentials; } - + JSONArray jsonArray = new JSONArray(json); - + for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String username = jsonObject.getString("username"); String password = jsonObject.getString("password"); String service = jsonObject.getString("service"); - + credentials.add(new Credential(username, password, service)); } - + return credentials; } - + private String credentialsToJson(List credentials) throws JSONException { JSONArray jsonArray = new JSONArray(); - + for (Credential credential : credentials) { JSONObject jsonObject = new JSONObject(); jsonObject.put("username", credential.getUsername()); jsonObject.put("password", credential.getPassword()); jsonObject.put("service", credential.getService()); - + jsonArray.put(jsonObject); } - + return jsonArray.toString(); } -} \ No newline at end of file +}