From c769b3f100058fec3f648f054df706b23b257f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rodri=CC=81guez?= Date: Wed, 11 Dec 2024 19:13:42 +0100 Subject: [PATCH 1/4] (android) Migrate from android-database-sqlcipher to sqlcipher-android --- android/build.gradle | 2 +- .../database/sqlite/SQLite/Database.java | 25 +++++--------- .../database/sqlite/SQLite/UtilsDrop.java | 18 +++++----- .../sqlite/SQLite/UtilsSQLCipher.java | 33 ++++++++++--------- .../database/sqlite/SQLite/UtilsSQLite.java | 6 ++-- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index b106a1a1..07bce56b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -61,7 +61,7 @@ dependencies { androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation 'net.zetetic:android-database-sqlcipher:4.5.3' + implementation 'net.zetetic:sqlcipher-android:4.6.1@aar' implementation "androidx.sqlite:sqlite:2.4.0" //security library implementation "androidx.security:security-crypto:1.1.0-alpha06" diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java index 5a477ae4..c4fd3005 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java @@ -35,9 +35,8 @@ import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.sqlcipher.Cursor; -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import net.zetetic.database.sqlcipher.SQLiteCursor; +import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -115,7 +114,7 @@ public Database( * Initialize the SQLCipher Libraries */ private void InitializeSQLCipher() { - SQLiteDatabase.loadLibs(_context); + System.loadLibrary("sqlcipher"); } public SupportSQLiteDatabase getDb() { @@ -247,7 +246,7 @@ public void open() throws Exception { if (_mode.equals("encryption")) { if (_isEncryption) { try { - _uCipher.encrypt(_context, _file, SQLiteDatabase.getBytes(password.toCharArray())); + _uCipher.encrypt(_context, _file, password.getBytes()); } catch (Exception e) { String msg = "Failed in encryption " + e.getMessage(); Log.v(TAG, msg); @@ -260,7 +259,7 @@ public void open() throws Exception { if (_mode.equals("decryption")) { if (_isEncryption) { try { - _uCipher.decrypt(_context, _file, SQLiteDatabase.getBytes(password.toCharArray())); + _uCipher.decrypt(_context, _file, password.getBytes()); password = ""; } catch (Exception e) { String msg = "Failed in decryption " + e.getMessage(); @@ -273,9 +272,9 @@ public void open() throws Exception { } try { if (!isNCDB() && !this._readOnly) { - _db = SQLiteDatabase.openOrCreateDatabase(_file, password, null); + _db = SQLiteDatabase.openOrCreateDatabase(_file, password, null, null); } else { - _db = SQLiteDatabase.openDatabase(String.valueOf(_file), password, null, SQLiteDatabase.OPEN_READONLY); + _db = SQLiteDatabase.openDatabase(String.valueOf(_file), password, null, SQLiteDatabase.OPEN_READONLY, null); } if (_db != null) { if (_db.isOpen()) { @@ -301,12 +300,6 @@ public void open() throws Exception { close(); _db = null; throw new Exception(msg); - } catch (SQLiteException e) { - String msg = "Failed in setVersion " + e.getMessage(); - Log.v(TAG, msg); - close(); - _db = null; - throw new Exception(msg); } if (_version > curVersion && _vUpgObject != null && _vUpgObject.size() > 0) { // if (_vUpgObject != null && _vUpgObject.size() > 0) { @@ -973,12 +966,12 @@ public String deleteSQL(Database mDB, String statement, ArrayList values */ public JSArray selectSQL(String statement, ArrayList values) throws Exception { JSArray retArray = new JSArray(); - Cursor c = null; + SQLiteCursor c = null; if (_db == null) { return retArray; } try { - c = (Cursor) _db.query(statement, values.toArray(new Object[0])); + c = (SQLiteCursor) _db.query(statement, values.toArray(new Object[0])); while (c.moveToNext()) { JSObject row = new JSObject(); for (int i = 0; i < c.getColumnCount(); i++) { diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsDrop.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsDrop.java index 5a91fcf0..8342a897 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsDrop.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsDrop.java @@ -6,7 +6,7 @@ import java.util.Dictionary; import java.util.Enumeration; import java.util.List; -import net.sqlcipher.Cursor; +import net.zetetic.database.sqlcipher.SQLiteCursor; public class UtilsDrop { @@ -20,7 +20,7 @@ public class UtilsDrop { public List getTablesNames(Database db) throws Exception { List tables = new ArrayList(); - Cursor cursor = null; + SQLiteCursor cursor = null; String query = "SELECT name FROM sqlite_master WHERE "; query += "type='table' AND name NOT LIKE 'sync_table' "; query += "AND name NOT LIKE '_temp_%' "; @@ -28,7 +28,7 @@ public List getTablesNames(Database db) throws Exception { query += "AND name NOT LIKE 'android_%' "; query += "ORDER BY rootpage DESC;"; try { - cursor = (Cursor) db.getDb().query(query); + cursor = (SQLiteCursor) db.getDb().query(query); cursor.moveToFirst(); while (!cursor.isAfterLast()) { String tableName = cursor.getString(0); @@ -51,12 +51,12 @@ public List getTablesNames(Database db) throws Exception { public List getViewNames(Database db) throws Exception { List views = new ArrayList(); - Cursor cursor = null; + SQLiteCursor cursor = null; String query = "SELECT name FROM sqlite_master WHERE "; query += "type='view' AND name NOT LIKE 'sqlite_%' "; query += "ORDER BY rootpage DESC;"; try { - cursor = (Cursor) db.getDb().query(query); + cursor = (SQLiteCursor) db.getDb().query(query); cursor.moveToFirst(); while (!cursor.isAfterLast()) { String viewName = cursor.getString(0); @@ -113,10 +113,10 @@ public void dropViews(Database db) throws Exception { public List getIndexesNames(Database db) { List indexes = new ArrayList(); - Cursor cursor = null; + SQLiteCursor cursor = null; String query = "SELECT name FROM sqlite_master WHERE "; query += "type='index' AND name NOT LIKE 'sqlite_%';"; - cursor = (Cursor) db.getDb().query(query); + cursor = (SQLiteCursor) db.getDb().query(query); cursor.moveToFirst(); while (!cursor.isAfterLast()) { String indexName = cursor.getString(0); @@ -152,10 +152,10 @@ public void dropIndexes(Database db) throws Exception { public List getTriggersNames(Database db) { List triggers = new ArrayList(); - Cursor cursor = null; + SQLiteCursor cursor = null; String query = "SELECT name FROM sqlite_master WHERE "; query += "type='trigger';"; - cursor = (Cursor) db.getDb().query(query); + cursor = (SQLiteCursor) db.getDb().query(query); cursor.moveToFirst(); while (!cursor.isAfterLast()) { String triggerName = cursor.getString(0); diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java index e6f24e34..ffbbfe28 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java @@ -5,9 +5,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; -import net.sqlcipher.database.SQLiteStatement; +import net.zetetic.database.sqlcipher.SQLiteDatabase; +import net.zetetic.database.sqlcipher.SQLiteStatement; public class UtilsSQLCipher { @@ -36,12 +35,12 @@ public enum State { * @return the detected state of the database */ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences sharedPreferences, GlobalSQLite globVar) { - SQLiteDatabase.loadLibs(ctxt); + System.loadLibrary("sqlcipher"); if (dbPath.exists()) { SQLiteDatabase db = null; try { - db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READONLY); + db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READONLY, null); db.getVersion(); @@ -50,7 +49,7 @@ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences share try { String passphrase = sharedPreferences.getString("secret", ""); if (passphrase.length() > 0) { - db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READONLY); + db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READONLY, null); db.getVersion(); return (State.ENCRYPTED_SECRET); } else { @@ -59,7 +58,7 @@ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences share } catch (Exception e1) { try { if (globVar.secret.length() > 0) { - db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), globVar.secret, null, SQLiteDatabase.OPEN_READONLY); + db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), globVar.secret, null, SQLiteDatabase.OPEN_READONLY, null); db.getVersion(); return (State.ENCRYPTED_GLOBAL_SECRET); } else { @@ -92,11 +91,11 @@ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences share * @throws IOException */ public void encrypt(Context ctxt, File originalFile, byte[] passphrase) throws IOException { - SQLiteDatabase.loadLibs(ctxt); + System.loadLibrary("sqlcipher"); if (originalFile.exists()) { File newFile = File.createTempFile("sqlcipherutils", "tmp", ctxt.getCacheDir()); - SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE); + SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE, null); int version = db.getVersion(); db.close(); @@ -135,7 +134,7 @@ public void encrypt(Context ctxt, File originalFile, byte[] passphrase) throws I } public void decrypt(Context ctxt, File originalFile, byte[] passphrase) throws IOException { - SQLiteDatabase.loadLibs(ctxt); + System.loadLibrary("sqlcipher"); if (originalFile.exists()) { // Create a temporary file for the decrypted database in the cache directory @@ -146,7 +145,8 @@ public void decrypt(Context ctxt, File originalFile, byte[] passphrase) throws I decryptedFile.getAbsolutePath(), "", null, - SQLiteDatabase.OPEN_READWRITE + SQLiteDatabase.OPEN_READWRITE, + null ); // Open the encrypted database with the provided passphrase @@ -154,7 +154,8 @@ public void decrypt(Context ctxt, File originalFile, byte[] passphrase) throws I originalFile.getAbsolutePath(), new String(passphrase), null, - SQLiteDatabase.OPEN_READWRITE + SQLiteDatabase.OPEN_READWRITE, + null ); int version = encryptedDb.getVersion(); @@ -196,14 +197,14 @@ public void decrypt(Context ctxt, File originalFile, byte[] passphrase) throws I } } - public void changePassword(Context ctxt, File file, String password, String nwpassword) throws IOException { - SQLiteDatabase.loadLibs(ctxt); + public void changePassword(Context ctxt, File file, String password, String nwpassword) throws Exception { + System.loadLibrary("sqlcipher"); if (file.exists()) { - SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), password, null, SQLiteDatabase.OPEN_READWRITE); + SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), password, null, SQLiteDatabase.OPEN_READWRITE, null); if (!db.isOpen()) { - throw new SQLiteException("database " + file.getAbsolutePath() + " open failed"); + throw new Exception("database " + file.getAbsolutePath() + " open failed"); } db.changePassword(nwpassword); db.close(); diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java index 202d2912..f1c3d15c 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import net.sqlcipher.Cursor; +import net.zetetic.database.sqlcipher.SQLiteCursor; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -19,7 +19,7 @@ public int dbChanges(SupportSQLiteDatabase db) { String SELECT_CHANGE = "SELECT total_changes()"; Boolean success = true; int ret = Integer.valueOf(-1); - Cursor cursor = (Cursor) db.query(SELECT_CHANGE, null); + SQLiteCursor cursor = (SQLiteCursor) db.query(SELECT_CHANGE, null); if (cursor != null) { if (cursor.moveToFirst()) { ret = Integer.parseInt(cursor.getString(0)); @@ -33,7 +33,7 @@ public long dbLastId(SupportSQLiteDatabase db) { String SELECT_CHANGE = "SELECT last_insert_rowid()"; Boolean success = true; long ret = (long) -1; - Cursor cursor = (Cursor) db.query(SELECT_CHANGE, null); + SQLiteCursor cursor = (SQLiteCursor) db.query(SELECT_CHANGE, null); if (cursor.moveToFirst()) { ret = Long.parseLong(cursor.getString(0)); } From 3a1cdc45aa1cfba8fdec7fc9800a7f1f9b38e8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rodri=CC=81guez?= Date: Wed, 11 Dec 2024 19:32:13 +0100 Subject: [PATCH 2/4] code style --- .../community/database/sqlite/SQLite/UtilsSQLCipher.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java index ffbbfe28..0ccafd56 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java @@ -58,7 +58,14 @@ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences share } catch (Exception e1) { try { if (globVar.secret.length() > 0) { - db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), globVar.secret, null, SQLiteDatabase.OPEN_READONLY, null); + db = + SQLiteDatabase.openDatabase( + dbPath.getAbsolutePath(), + globVar.secret, + null, + SQLiteDatabase.OPEN_READONLY, + null + ); db.getVersion(); return (State.ENCRYPTED_GLOBAL_SECRET); } else { From d33cd2373948c3945c3fb3d6acdffa3f95579118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rodri=CC=81guez?= Date: Mon, 19 May 2025 19:01:41 +0200 Subject: [PATCH 3/4] Explicitly specify password charset --- .../community/database/sqlite/SQLite/Database.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java index c4fd3005..ecce9267 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java @@ -26,6 +26,7 @@ import com.getcapacitor.community.database.sqlite.SQLite.ImportExportJson.UtilsEncryption; import com.getcapacitor.community.database.sqlite.SQLite.ImportExportJson.UtilsJson; import java.io.File; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -246,7 +247,7 @@ public void open() throws Exception { if (_mode.equals("encryption")) { if (_isEncryption) { try { - _uCipher.encrypt(_context, _file, password.getBytes()); + _uCipher.encrypt(_context, _file, password.getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { String msg = "Failed in encryption " + e.getMessage(); Log.v(TAG, msg); From bf54d286fe1f394651d783a60a1ba4f27641d86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rodri=CC=81guez?= Date: Wed, 21 May 2025 16:42:55 +0200 Subject: [PATCH 4/4] style: format --- .../database/sqlite/SQLite/UtilsSQLCipher.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java index 0ccafd56..1585bc25 100644 --- a/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java +++ b/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java @@ -58,14 +58,13 @@ public State getDatabaseState(Context ctxt, File dbPath, SharedPreferences share } catch (Exception e1) { try { if (globVar.secret.length() > 0) { - db = - SQLiteDatabase.openDatabase( - dbPath.getAbsolutePath(), - globVar.secret, - null, - SQLiteDatabase.OPEN_READONLY, - null - ); + db = SQLiteDatabase.openDatabase( + dbPath.getAbsolutePath(), + globVar.secret, + null, + SQLiteDatabase.OPEN_READONLY, + null + ); db.getVersion(); return (State.ENCRYPTED_GLOBAL_SECRET); } else {