I am trying to encrypt data stored in my content provider with an app upgrade. I followed How do I implement SQLCipher when using SQLiteOpenHelper?. While this works on a new installation, if I upgrade the app it crashes:
net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3009)
at android.app.ActivityThread.access$1800(ActivityThread.java:177)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1526)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5951)
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:1388)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
Caused by: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
Deleting existing tables and creating new ones was of no help. How do I fix this? MyDBHelper class:
public class MyDBHelper extends SQLiteOpenHelper {
public MyDBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
SQLiteDatabase.loadLibs(context);
}
public static final String DATABASE_NAME = "mydb.db";
private static final int DATABASE_VERSION = 2; // before attempting encryption it was 1
@Override
public void onCreate(SQLiteDatabase db) {
createTables(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + "TEST_TABLE");
createTables(db);
}
private void createTables(SQLiteDatabase db){
db.execSQL(MyDBQueries.CREATE_TEST_TABLE);
}
}
My updated provider uses a key to open the database:
SQLiteDatabase db = databaseHelper.getWritableDatabase("encryptionKey");
Database is not encrypted initially, I want that to be encrypted with app upgrade
That is not going to happen automatically, and it cannot happen as part of SQLiteOpenHelper and its onUpgrade() path (as by the time onUpgrade() is called, SQLiteOpenHelper will have already attempted to open the unencrypted database, which will fail with the exception that you have shown).
You will need to separately encrypt that database. This code is what I have used:
public static void encrypt(Context ctxt, String dbName,
String passphrase) throws IOException {
File originalFile=ctxt.getDatabasePath(dbName);
if (originalFile.exists()) {
File newFile=
File.createTempFile("sqlcipherutils", "tmp",
ctxt.getCacheDir());
SQLiteDatabase db=
SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
"", null,
SQLiteDatabase.OPEN_READWRITE);
db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
newFile.getAbsolutePath(), passphrase));
db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
db.rawExecSQL("DETACH DATABASE encrypted;");
int version=db.getVersion();
db.close();
db=
SQLiteDatabase.openDatabase(newFile.getAbsolutePath(),
passphrase, null,
SQLiteDatabase.OPEN_READWRITE);
db.setVersion(version);
db.close();
originalFile.delete();
newFile.renameTo(originalFile);
}
}
After encrypt() returns, then you can go ahead and try to open the database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With