I am using SQLiteCipher to encrypt my database. Previously I was using db.execsql() statement which was working fine. Now I changed the query to SQLStatment.
Here is my code
private static void encrypt(Context ctxt) {
File originalFile = ctxt.getDatabasePath(DBNAME);
if (originalFile.exists()) {
File newFile;
try {
newFile = File.createTempFile("sqlcipherutils", "tmp", ctxt.getCacheDir());
SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE);
SQLiteStatement preparedStatement = db.compileStatement("ATTACH DATABASE ? AS encrypted KEY ?");
preparedStatement.bindString(1, newFile.getAbsolutePath());
preparedStatement.bindString(2, DataControllers.getDbKey());
preparedStatement.execute();
SQLiteStatement preparedStatement1= db.compileStatement("SELECT sqlcipher_export('encrypted')");
preparedStatement1.execute();
SQLiteStatement preparedStatement2= db.compileStatement("DETACH DATABASE encrypted");
preparedStatement2.execute();
int version = db.getVersion();
db.close();
db = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), DataControllers.getDbKey(), null, SQLiteDatabase.OPEN_READWRITE);
db.setVersion(version);
db.close();
originalFile.delete();
newFile.renameTo(originalFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
First execute statements runs but 2nd execute statement throws exception.
Here is stack trace
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.package/com.package.ui.Dashboard}: net.sqlcipher.database.SQLiteException: error code 100: another row available
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2327)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2392)
at android.app.ActivityThread.access$800(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1305)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5293)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: net.sqlcipher.database.SQLiteException: error code 100: another row available
at net.sqlcipher.database.SQLiteStatement.native_execute(Native Method)
at net.sqlcipher.database.SQLiteStatement.execute(SQLiteStatement.java:58)
at com.package.dbconnections.DatabaseOpenHelper.encrypt(DatabaseOpenHelper.java:172)
at com.package.dbconnections.DatabaseOpenHelper.isDbEncrypted(DatabaseOpenHelper.java:151)
at com.package.dbconnections.DatabaseOpenHelper.getInstance(DatabaseOpenHelper.java:136)
at com.package.dbconnections.DatabaseOpenHelper.getUrls(DatabaseOpenHelper.java:605)
I believe that the only resolution is to use the specially created rawExecSQL, method.
There is no security issue with using this as there is no chance for SQL Injection as there is no user input. perhaps see SQL injection.
I believe the issue is that in general exec/execute allow limited results, rawQuery/query will return a Cursor. I believe that the conversion is probably generating SQL, modifying it by encrypting the data and then executing that resultant SQL as a stream of statements (hence the error code 100 when trying to use execute). A special method (hence rawExecSQL) is required as most of the in-built methods only allow a single statement to be run.
Working Example
Here's a working example, with other attempts commented out with the result (including the error code 100 if trying to use SQLiteStatement).
The example creates the normal database, loads some data, extracts and dumps the data (for comparison/proof) closes it using the stock android SQLiteDatabase methods.
The encrypted database is then created using the SQlCipher openorcreate method and then immediately closed (thus creating the file).
The normal database is then opened with the SQLCipher methods, the newly created empty encrypted database is then attached, the conversion then done and the encrypted database is detached. The normal is then closed.
Finally the new encrypted database is open, the data extracted and dumped (for comparison/proof).
The code is :-
Result :-
Part 1 - prior to the conversion/encryption :-
Part 2 after encryption from the encrypted dataabse.