2

I am trying to encrypt data stored in my content provider with App upgrade. I followed below link for this,
How to implement SQLCipher when using SQLiteOpenHelper

While this works perfectly fine if a new installation is done, If I go for an upgrade, app crashes with below error

    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


I even tried to delete the existing tables and create a new table just to see if this solved the problem but no that was of no help either. Please suggest how to fix this, My Helper class is as below,

                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 simply uses a key now to open the db as below,

                SQLiteDatabase db = databaseHelper.getWritableDatabase("encryptionKey");


As I mentioned, fresh installs works good, but upgrade crashes the app. Please suggest how to fix this.

Community
  • 1
  • 1
Kaps
  • 2,345
  • 2
  • 26
  • 37
  • Are you upgrading from a database that is already encrypted? Or are you upgrading from a database that is not yet encrypted? BTW, in the future, please post the entire stack trace. – CommonsWare Jan 21 '16 at 12:49
  • Database is not encrypted initially, I want that to be encrypted with app upgrade. – Kaps Jan 21 '16 at 12:51

1 Answers1

6

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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thank you for the valuable information. So, where do I call this utility? Further, how to ensure that it does not impact fresh installs of the app, because fresh install will not need this. – Kaps Jan 21 '16 at 14:12
  • 1
    @Kaps: "So, where do I call this utility?" -- sometime after `loadLibs()` and sometime before `getReadableDatabase()` or `getWriteableDatabase()`. "Further, how to ensure that it does not impact fresh installs of the app, because fresh install will not need this" -- see if the database file already exists. – CommonsWare Jan 21 '16 at 14:21
  • @CommonsWare, shouldn't you be creating the encrypted database in a different file and not renaming it to the original file? because in the subsequent runs, the `originalFile.exists()` will always be `true`. Instead isn't it better if the encrypted file is generated in another file instead? – Avinash R Jun 09 '16 at 14:10
  • @AvinashR: This code presumes that you know that the database needs to be encrypted. The problem with changing to a different filename is that `SQLiteOpenHelper` wants to know the filename in the constructor, which makes it a bit complicated to create your `SQLiteOpenHelper` subclass. You are certainly welcome to have the encrypted file use a different name if you want; that is not how I approached this particular bit of sample code. – CommonsWare Jun 09 '16 at 14:12
  • getting exception "net.sqlcipher.database.SQLiteException: error code 14: Could not open database". Please can someone help me! Thanks – sandeepmaaram Feb 18 '18 at 15:58
  • @sandeepmaaram: Ask a separate Stack Overflow question, where you provide a [mcve]. This would include the complete Java stack trace along with your code that is generating the crash. – CommonsWare Feb 18 '18 at 15:59