You won't be able to open the existing database file if you'll try to upgrade the SQLCipher libraries without migrating it (the database file) first. The following error will be thrown in attempt to open existing database:
03-29 16:14:04.199 18107-18107/info.osom.sqlciphertest I/Database? sqlite returned: error code = 26, msg = file is encrypted or is not a databaseYou have two options to solve that. The first one is to reset iteration count back to 4000 using appropriate PRAGMA setting. The second option is to migrate the database in place using PRAGMA cipher_migrate setting. I will demonstrate below how to implement the cipher_migrate in your code.
03-29 16:14:04.199 18107-18107/info.osom.sqlciphertest E/Database? CREATE TABLE android_metadata failed
03-29 16:14:04.199 18107-18107/info.osom.sqlciphertest E/Database? Failed to setLocale() when constructing, closing the database
net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
at net.sqlcipher.database.SQLiteDatabase.native_setLocale(Native Method)
at net.sqlcipher.database.SQLiteDatabase.setLocale(SQLiteDatabase.java:2092)
at net.sqlcipher.database.SQLiteDatabase.(SQLiteDatabase.java:1958)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:875)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:907)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:99)
at info.osom.sqlciphertest.MainActivity$PlaceholderFragment$1.onClick(MainActivity.java:69)
at android.view.View.performClick(View.java:4202)
at android.view.View$PerformClick.run(View.java:17340)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Generally speaking, all you have to do is implement the SQLiteDatabaseHook interface that executes the cipher_migrate PRAGMA command on the existing database. It should be executing only once, and therefore you have to maintain a flag that tells you whether you already migrated or not. Initially, passing the instance of the class was not so convenient, and Mark Murphy proposed alternative for SQLiteOpenHelper class. Eventually, SQLCipher guys added another constructor that receives implementation of SQLiteDatabaseHook as a parameter.
In my solution, I slightly modificated Mark's implementation of SQLiteDatabaseHook:
Passing the SQLCipherV3Helper instance as a fifth argument to the SQLiteOpenHelper constructor:
Additional relevant links:
SQLCipher 3.0.0 release statement
Hi Alex
ReplyDeleteI've implemented you code above but unfortunately i'm still getting the usual 'file is encrypted or not a database' error.
Do i have to explicitly call any methods or do i simply let the DBHelper constuctutor with 5 params execute?
thanks
Matt
Hi Matt, 'file is encrypted or is not a database' error can be thrown in many flows, while this post focusing on aforementioned error only in context of upgrade to 3.x version. Can you confirm that this is your case?
Delete