SQLCipher for Android Application Integration

The community edition of SQLCipher for Android is distributed via the AAR package format allowing usage in either Java or Kotlin-based Android projects. Integration within Gradle can be performed by adding the following entry to the dependencies section of the app/build.gradle file:

implementation 'net.zetetic:android-database-sqlcipher:4.5.1@aar'
implementation 'androidx.sqlite:sqlite:2.0.1'

This tutorial will cover integrating the binaries of SQLCipher for Android into an existing Android application. This tutorial assumes the developer has the latest SQLCipher for Android commercial

We need to copy the AAR library file over into the app libs directory of our application. Execute the following commands:

% cp android-database-sqlcipher-4.5.1.aar app/libs

Next, modify the project’s build.gradle file, adding the following flatDir

allProjects {
  repositories {
    jcenter()
    flatDir {
      dirs 'libs'
    }

  }
}

Within the app module build.gradle, add the following to the dependencies block:

implementation (name: 'android-database-sqlcipher-4.5.1', ext: 'aar')
implementation 'androidx.sqlite:sqlite:2.0.1'

For Commercial Edition only, the license code may be provided when opening a connection via SQLiteDatabaseHook. Below is an example:

SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
  public void preKey(SQLiteDatabase database) {
    database.rawExec(String.format("PRAGMA cipher_license = '%s';", LICENSE_CODE));
  }
  public void postKey(SQLiteDatabase database) {}
};
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databasePath.getAbsolutePath(), password, null, hook);

Copy the AAR package into app/libs as described above. Within the app module build.gradle, add the following to the dependencies block:

implementation files('libs/sqlcipher-android-4.5.1-release.aar')
implementation 'androidx.sqlite:sqlite:2.1.0'

🔥 Hot Tip: Make sure to remove any previous SQLCipher for Android references from build.gradle.

For Commercial Edition only, the license code may be provided when opening a connection via SQLiteDatabaseHook. Below is an example:

SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
  public void preKey(SQLiteConnection connection) {
    connection.execute(String.format("PRAGMA cipher_license = '%s';", LICENSE_CODE), new Object[]{}, null);
  }
  public void postKey(SQLiteConnection connection) {}
};
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databasePath.getAbsolutePath(), password, null, hook);

Next we will modify the source of the activity to properly initialize the native libraries for SQLCipher and then create our database file inserting a record. In particular, note the import of net.sqlcipher.database.SQLiteDatabase instead of android.database.sqlite.SQLiteDatabase as well as the call to SQLiteDatabase.loadLibs(this). The call to SQLiteDatabase.loadLibs(this) must occur before any other database operation. The following is a usage example in Java:

package com.demo.sqlcipher;

import java.io.File;
import net.sqlcipher.database.SQLiteDatabase;
import android.app.Activity;
import android.os.Bundle;

public class HelloSQLCipherActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        InitializeSQLCipher();
    }

    private void InitializeSQLCipher() {
        SQLiteDatabase.loadLibs(this);
        File databaseFile = getDatabasePath("demo.db");
        databaseFile.mkdirs();
        databaseFile.delete();
        SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "test123", null);
        database.execSQL("create table t1(a, b)");
        database.execSQL("insert into t1(a, b) values(?, ?)", new Object[]{"one for the money",
                                                                        "two for the show"});
    }
}

SQLCipher for Android can be seamlessly used with Kotlin as well:

package com.demo.sqlcipher

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import net.sqlcipher.database.SQLiteDatabase

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        SQLiteDatabase.loadLibs(this)
        val databaseFile = getDatabasePath("demo.db")
        if(databaseFile.exists()) databaseFile.delete()
        databaseFile.mkdirs()
        databaseFile.delete()
        val database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "test123", null)
        database.execSQL("create table t1(a, b)")
        database.execSQL("insert into t1(a, b) values(?, ?)",
            arrayOf<Any>("one for the money", "two for the show")
        )
    }
}

The application should now be able to run on either an emulator or device.

The application should import the net.zetetic.database.sqlcipher classes. Please note, if subclassing a SQLiteOpenHelper, call setWriteAheadLoggingEnabled(true) to enable WAL journal mode and improve performance.

package com.demo.sqlcipher

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import net.zetetic.database.sqlcipher.SQLiteDatabase;

public class HelloSQLCipherActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        InitializeSQLCipher();
    }

    private void InitializeSQLCipher() {
        System.loadLibrary("sqlcipher");
        File databaseFile = getDatabasePath("demo.db");
        SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, password, null, null, null);
        database.execSQL("create table t1(a, b)");
        database.execSQL("insert into t1(a, b) values(?, ?)", new Object[]{"one for the money",
                                                                        "two for the show"});
    }
}

To perform operations on the database instance immediately before or after the keying operation is performed, provide a SQLiteDatabaseHook instance when creating your database connection:

SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
      public void preKey(SQLiteConnection connection) { }
      public void postKey(SQLiteConnection connection) { }
    };