Migrating from SQLCipher 3.5.1 to 4.1.3 in Android

I have a database created with SQLCipher 3.5.1. I tried migrating it to SQLCipher 4.1.3 using PRAGMA cipher_migrate according to the guidelines, but even though the database opens immediately after the openDatabase() call, it does not actually migrate.

You can find the code I used below:

import android.content.Context;

import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;

public class DatabaseMigrator {

    static public void checkAndMigrateDatabase(Context context, String databaseName, String password) {

        String path = context.getDatabasePath(databaseName).getPath();

        SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
            @Override
            public void postKey(SQLiteDatabase database) {

                Cursor c = database.rawQuery("PRAGMA cipher_migrate", null);

                Boolean migrationOccurred = false;

                if (c.getCount() == 1) {
                    c.moveToFirst();
                    String selection = c.getString(0);

                    migrationOccurred = selection.equals("0");

                    print("selection: " + selection);
                }

                c.close();

                print("migrationOccurred: " + migrationOccurred);
            }

            @Override
            public void preKey(SQLiteDatabase database) { }
        };

        SQLiteDatabase database = null;

        try {
            database = SQLiteDatabase.openDatabase(path, password, null, 0, hook);
        }
        catch (Exception e) {
            print("Exception while trying to open db: " + e);
        }

        if (database != null) {

            database.close();
        }
    }
}

Also, although the guidelines state that the value of the PRAGMA cipher_migrate SQL result should be 0 after the migration, in my case it’s always 1.

Next time I try to open the DB (on the next app launch) without the PRAGMA cipher_migrate command, I get the “not a database” exception and upon opening the db using the DB Browser for SQLite app, I still have to use the SQLiteCipher 3 defaults, which indicates that the migration never happened.

On top of that, I managed to successfully open the db using PRAGMA cipher_compatibility=3, so it looks like there’s an issue during the migration process.

Does anyone have any hints on how PRAGMA cipher_migrate can be performed successfully so it’s not needed every time the app launches?

1 Like

Hi @petrakeas

We have a large set of tests that cover various migration scenarios in SQLCipher here. Would you try running the SQLCipher for Android test suite, possibly modifying the Android PRAGMA cipher_migrate test to utilize your database file and report back?

Hi Nick,

After checking out the tests, it seems that for the migration to succeed we either have to use the openOrCreateDatabase() method, or the openDatabase() one passing the CREATE_IF_NECESSARY flag instead of the OPEN_READWRITE (0) one.

If we use openDatabase with the OPEN_READWRITE (0) flag, the problem that I described can be reproduced even in the SQLCipher Android Tests repo.

Is this the expected behavior?

Hello @petrakeas,

When performing a PRAGMA cipher_migrate command, SQLCipher will internally attach a migration database temporarily to move/rewrite the content to the latest format. The ATTACH logic will utilize the flags provided to open the initial database instance for the attached database. Thus, in your case you would need to open your database calling openDatabase, but passing SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY as the flags value for migration. Would you give that a try and let us know your results? Thanks!

Hi Nick,

If I understand correctly you are proposing what I already described in my previous comment.

Using openDatabase() with CREATE_IF_NECESSARY before passing "PRAGMA cipher_migrate", migrates the DB successfully.

From your answer it seems that CREATE_IF_NECESSARY is needed so that the temporary DB is created.

I think it would be a good idea for the migration guide to be updated, to point that this flag (CREATE_IF_NECESSARY), or that the openOrCreateDatabase() method are required for the migration to succeed.

Hello @petrakeas

Thank you for your follow-up, that is correct. We will review the documentation and update accordingly. Thanks!

2 Likes

Hello @developernotes,

Recently we are also trying to migrate from sqlcipher 3 to v4 as per your latest upgrade to net.zetetic:sqlcipher-android:4.5.6@aar, but its giving same error as mentioned by @petrakeas . I know its old thread for this discussion but we need your urgent help in this. We have also followed your suggestion given in the post How to encrypt a plaintext SQLite database to use SQLCipher (and avoid “file is encrypted or is not a database” errors) , but still getting below error

Exception occured while getting valid activeSessionandroid.database.sqlite.SQLiteException: file is not a database (code 26): , while compiling: SELECT COUNT(*) FROM sqlite_schema;

Hi @abhijeet143,

If you are upgrading from SQLCipher 3.x to SQLCipher 4.x, please review the upgrade guidance as SQLCipher 4 will not open older databases by default.

Hi @developernotes Thank you for replying back with the guidance at the earliest. I am finally able to migrate the database from SQLCipher 3.x to SQLCipher 4.x after using the library version 4.5.4@aar. It does not worked at all even after trying a lot with 4.5.6@aar, because in this version SQLiteDatabaseHook having methods preKey() & postKey() are not accepting the SQLiteDatabase, instead it accepts SQLiteConnection as parameters. So, the solution given by you in the link
sqlcipher-android-tests/app/src/main/java/net/zetetic/tests/CipherMigrateTest.java at master · sqlcipher/sqlcipher-android-tests · GitHub
doesn’t help until we rollback the version of SQLCipher to 4.5.4@aar. It will be great if you provide latest update as per 4.5.6@aar for migrating from 3x to 4x, as its the latest lib version as given in link SQLCipher for Android - Full Database Encryption for SQLite on Android | Zetetic. Thanks.

Hi @abhijeet143,

It sounds like you were also trying to migrate from the deprecated library android-database-sqlcipher to sqlcipher-android. We have some online documentation about migrating the API’s here. Please keep in mind that you will still need to take into account the adjustments I previously mentioned when moving to SQLCipher 4 regardless of the client library you use.

Hi @developernotes,

Yes, for us we were trying to migrate from the deprecated library & also referred the link guided by you as per SQLCipher for Android Migration - How to Convert Applications from android-database-sqlcipher to sqlcipher-android | Zetetic. We thought of using the latest version i.e. 4.5.6@aar for bin up to date, but it only provides the underlying SQLiteConnection object for creation of the SQLiteDatabaseHook. That’s where we got stuck searching for the answers. Can you please guide if there’s any possible way we can achieve the migration to 4.x using 4.5.6@aar? or its limited to 4.5.4@aar only?
Thanks

Hi @abhijeet143,

The android-database-sqlcipher was deprecated last year and thus the latest Community edition of that library is 4.5.4. Moving forward, the sqlcipher-android is the long-term replacement library and is available with the current SQLCipher release of 4.5.6. Portions of the API and package names have changed when migrating from android-database-sqlcipher to sqlcipher-android, we recommend reviewing the migration guide. The SQLiteConnection can perform the same type of operations within a SQLiteDatabaseHook using the sqlcipher-android library that you would perform with android-database-sqlcipher.

Hi @developernotes,

Big thanks for clarifying the doubts about latest v4.5.6. I will follow the guide provided by you.