Room Database Not Getting Encrypted

I have tried to follow all the examples out there for encrypting a Android Room Database. The code executes, but when I check the state, it comes back as unencrypted. I do not see nay errors in the log.

The following are the libraries I am using.

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import net.sqlcipher.database.SQLiteDatabaseHook;

The following is my application code

static CheckingInRoomDatabase getDatabase(final Context context) {

    Log.i(TAG, "CheckingInRoomDatabase getDatabase: In Checking In Room Database");
    if (INSTANCE == null) {
        Log.i(TAG, "CheckingInRoomDatabase getDatabase Instance == null first: ");
        synchronized (CheckingInRoomDatabase.class) {
            if (INSTANCE == null) {
                Log.i(TAG, "CheckingInRoomDatabase getDatabase Instance == null second:   ");
                char[] ch = AppConfig.PASSPHRASE;
                // Declaring a byte array
                byte[] passPhrase = new byte[ch.length];
                // Iterating over the char array
                for (int i = 0; i < ch.length; i++) {
                    // Converting each char into its byte equivalent
                    passPhrase[i] = (byte) ch[i];
                }
                String testStatus = AppConfig.DEBUG;
                if (testStatus.equals("Prod")) {
                    final SupportFactory factory = new SupportFactory(passPhrase);
                    Log.i(TAG, "CheckingInRoomDatabase getDatabase Instance == null testStatus= " + testStatus);
                    INSTANCE =
                            Room.databaseBuilder(context,
                                            CheckingInRoomDatabase.class,
                                            "checking_in_database.db")
                                    .fallbackToDestructiveMigration()
                                    .openHelperFactory(factory)
                                    .addCallback(sRoomDatabaseCallback)
                                    .build();
                    Log.i(TAG, "CheckingInRoomDatabase getDatabase: running populates from builder" + INSTANCE);

                    try {
                        // Get database state
                        SQLCipherUtils.State state = SQLCipherUtils.getDatabaseState(context, "checking_in_database.db");

                        // Check whether database is encrypted
                        if (state == SQLCipherUtils.State.UNENCRYPTED) {
                            Log.i(TAG, "CheckingInRoomDatabase getDatabase: database is not encrypted");
                            Bugfender.e("CheckingInRoomDatabase getDatabase", "database is not encrypted");

                        } else if (state == SQLCipherUtils.State.ENCRYPTED) {
                            // Otherwise, good to go
                            Log.i(TAG, "CheckingInRoomDatabase getDatabase: database is encrypted");
                            Bugfender.i("CheckingInRoomDatabase getDatabase ", "database is encrypted.  No action necessary.");
                        } else if (state == SQLCipherUtils.State.DOES_NOT_EXIST) {
                            // No database found.  Normal if first launch
                            Log.i(TAG, "CheckingInRoomDatabase getDatabase: database not found");
                            Bugfender.i("CheckingInRoomDatabase getDatabase ", "database not found");
                        }

                    } catch (Exception e) {
                        Bugfender.e("CheckingInRoomDatabase ", "Failed to get database encryption state!" + e);
                    }
                }
                else {
                    INSTANCE =
                            Room.databaseBuilder(context,
                                            CheckingInRoomDatabase.class,
                                            "checking_in_database.db")
                                    .fallbackToDestructiveMigration()
                                    .addCallback(sRoomDatabaseCallback)
                                    .build();
                }
              
            }
        }
        Log.i(TAG, "CheckingInRoomDatabase getDatabase: database being populated");
    }
    Log.i(TAG, "CheckingInRoomDatabase getDatabase database returned: ");
    return INSTANCE;

}

The following is my SupportFactory.class

package com.grgmobilesolutions.peepsconnection;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import net.sqlcipher.database.SQLiteDatabaseHook;

public class SupportFactory implements SupportSQLiteOpenHelper.Factory {
private final byte passphrase;
private final SQLiteDatabaseHook hook;
private final boolean clearPassphrase;

public SupportFactory(byte[] passphrase) {
    this(passphrase, (SQLiteDatabaseHook)null);
}

public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook) {
    this(passphrase, hook, true);
}

public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook,
                      boolean clearPassphrase) {
    this.passphrase = passphrase;
    this.hook = hook;
    this.clearPassphrase = clearPassphrase;
}

@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
    return new SupportHelper(configuration, passphrase, hook, clearPassphrase);
}

}

The following is my SupportHelper.class


//package com.grgmobilesolutions.peepsconnection;
package com.grgmobilesolutions.peepsconnection;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import net.sqlcipher.database.SQLiteDatabaseHook;

public class SupportHelper implements SupportSQLiteOpenHelper {

private SQLiteOpenHelper openHelper;

public SupportHelper(final Configuration configuration, byte[] password, SQLiteDatabaseHook hook,
                     boolean enableWriteAheadLogging) {
    this(configuration, password, hook, enableWriteAheadLogging, configuration.callback.version);
}

public SupportHelper(final Configuration configuration, byte[] password, SQLiteDatabaseHook hook,
                     boolean enableWriteAheadLogging, int minimumSupportedVersion) {

}

@Nullable
@Override
public String getDatabaseName() {
    return null;
}

@NonNull
@Override
public SupportSQLiteDatabase getReadableDatabase() {
    return null;
}

@NonNull
@Override
public SupportSQLiteDatabase getWritableDatabase() {
    return null;
}

@Override
public void close() {

}

@Override
public void setWriteAheadLoggingEnabled(boolean b) {

}

}


Hi @grgmobile,

To convert a SQLite plaintext database to a SQLCipher encrypted database file you will need to use the sqlcipher_export(...) [1] convenience function, for your case, you’ll want to review the SQL operations in example #1[2]. We have an example of the import process within the source code test suite here [3]. I would suggest moving this preprocessing logic outside of any Room API usage so that Room can assume your database file is already encrypted with SQLCipher.


  1. SQLCipher API - Zetetic ↩︎

  2. SQLCipher API - Zetetic ↩︎

  3. sqlcipher-android/sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/ImportUnencryptedDatabaseTest.java at master · sqlcipher/sqlcipher-android · GitHub ↩︎

I understand that for a process of taking an existing unencrypted database and encrypting it. Does that work for a database that has not been created yet?

Is there a process/example for starting out the database as encrypted, such as the first time a user loads the application and the database is created during the install? I thought from other examples that it was the combination of

SupportFactory factory = new SupportFactory(passphrase) and
builder.openHelperFactory(factory)

Thanks for your assistance.

Hi @grgmobile,

Yes, you can use the SupportOpenHelperFactory provided by sqlcipher-android to integrate SQLCipher with the Room API and create a SQLCipher encrypted database file from scratch. An example of the API usage can be found in the test suite here.

Thanks for your assistance. I am working though your suggestions. The current issue is a conflict with the libraries being used.

I am getting the following error when I run the build app.

2 files found with path ‘lib/arm64-v8a/libsqlcipher.so’ from inputs:

  • C:\Users\georg.gradle\caches\transforms-3\ed7ca701acbb37459dc1e611df3c5c95\transformed\jetified-android-database-sqlcipher-4.5.3\jni\arm64-v8a\libsqlcipher.so
  • C:\Users\georg.gradle\caches\transforms-3\d6c404e1d2cdcc47e9251fab27827d42\transformed\jetified-sqlcipher-android-4.5.5\jni\arm64-v8a\libsqlcipher.so
    If you are using jniLibs and CMake IMPORTED targets, see
    https://developer.android.com/r/tools/jniLibs-vs-imported-targets

My gradle build uses the following


implementation 'androidx.sqlite:sqlite:2.4.0'
implementation "net.zetetic:android-database-sqlcipher:4.5.3"
implementation 'net.zetetic:sqlcipher-android:4.5.5@aar'

I am importing the following in the app.

import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;

Hi @grgmobile,

android-database-sqlcipher is the legacy Android library which has been deprecated. sqlcipher-android is the long-term replacement library for Android. You cannot use both libraries within the application.

Sorry for all the questions.

So what would the import statement be for SQLiteDatabase and SQLiteStatement?

Hi @grgmobile,

When using sqlcipher-android, you can use the following import statements:

import net.zetetic.database.sqlcipher.SQLiteDatabase;
import net.zetetic.database.sqlcipher.SQLiteStatement;