How to apply SQLCipher to pre-existing Room database?

Hi. I’m having issues applying SQLCipher to a previously existing Room database.

SQLCipher works fine to new users who builds the database for the first time,
but for previous users whom already have a built a database, I’m getting the following runtime error.

Android Runtime: FATAL EXCEPTION: arch_disk_io_1
...
Caused by: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master

Before applying SQLCipher, room database is created by using the following code:

AppDatabase appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, “my-database”).build();

And this is how I applied SQLCipher:

char testBytes = {‘t’, ‘e’, ‘s’, ‘t’};
final byte passphrase = SQLiteDatabase.getBytes(testBytes);
final SupportFactory factory = new SupportFactory(passphrase);

AppDatabase appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, “my-database”).openHelperFactory(factory).build();

Thanks.

SQLCipher does not distinguish between “unencrypted database” and “encrypted database with the wrong passphrase”. Ideally, you would track whether or not the database is encrypted. And, at the time you decide that you want to encrypt an unencrypted database, you will need code to encrypt it. This Java method will encrypt an existing unencrypted database.

The getDatabaseState() method on that utility class will attempt to determine if a database is encrypted or not, by trying to open the database with an empty passphrase.

Wow thanks commonsguy!
I’ve got my DB encrypted, thanks to your response!!

1 Like

Hi
Can you please share your final code on how you encrypted your DB please?
Thank You!

Hi @samir

Are you looking to encrypt an existing plaintext SQLite database, or are you creating a new SQLCipher database from scratch? If the former, please take a look at the sqlcipher_export convenience function; for the latter we have documentation for using SQLCipher for Android here.

Hi,
Sorry for the delay in my reply

I have a room database and I have a somewhat same code as the post one
And I get the same error

What my doubt is that what should I do now?

call getDatabaseState() method and check if its encrypted or not? and if its not encrypted, encrypt it with the java method?

what does the databasebuilder with helperfactory do then?

Hi @samir

If your goal is to encrypt a plaintext SQLite database, then yes you will need to use sqlcipher_export(...) to encrypt the database using SQLCipher.

From our Room integration steps in the README, you will use the existing Room.databaseBuilder, but provide the openHelperFactory(...) with a SupportFactory instance which allows for setting the passphrase.

final byte[] passphrase = SQLiteDatabase.getBytes(userEnteredPassphrase);
final SupportFactory factory = new SupportFactory(passphrase);
final SomeDatabase room = Room.databaseBuilder(activity, SomeDatabase.class, DB_NAME)
  .openHelperFactory(factory)
  .build();

Thankyou so much for your reply. I have a room database.

I did create the db this way. i.e. via the code you gave. But when I was accessing the database, it was giving me “file is not a database…” error.

So as you suggested above, I ran the getDatabaseState function and it returned UNENCRYPTED. So I created an if loop

if (getDatabaseState(context, "db").toString().equals("UNENCRYPTED")) {
            try {
                encrypt(context, "db", passphrase);
            } catch (Exception e) {
                Log.e("RoomDatabase", e.getMessage());
            }
        }

And I placed the Room.databaseBuilder code after this condition. It returned me ENCRYPTED.

But now the issue is that when this database is used, the result which is supposed to happen, doesn’t happen. i.e. data does not get populated.

So my doubt here is that, is there some other step too that I need to follow after this?
as in decrypting the database or something?

I feel I am on the right track but if you can just guide me then it’ll be great

Thank you again.

Can you try pulling the database off the device after the cipher_migrate command is executed to further inspect it? You can use the nightly version of DB Browser for SQLite here to see if you are able to access it with either SQLCipher 3 or SQLCipher 4 settings in place.

Hi ,
i am also facing migration issue for existing room database. i have use SQLCipherUtils.encrypt for encrypting existing room database as mention in discussion. So expected behaviour should be database is encrypted and on query it should return old data. but look like database is getting encrypted but old is lost. on query i am not able fetch any data.

Adding code for reference

private fun create(context: Context, password: CharArray): MediDataBase {

        val state = SQLCipherUtils.getDatabaseState(context, DB_NAME)
        if (state == SQLCipherUtils.State.UNENCRYPTED){
            SQLCipherUtils.encrypt(
                    context,
                    DB_NAME,
                    password
            )
        }

        val supportFactory = SupportFactory(SQLiteDatabase.getBytes(password))
        return Room.databaseBuilder(context, MediDataBase::class.java, DB_NAME)
                .openHelperFactory(supportFactory)
                .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
                .fallbackToDestructiveMigration()
                .build()
    }