Deprecation Notice:The Legacy SQLCipher for Android API (android-database-sqlcipher
) has been officially deprecated. The long term replacement is the new SQLCipher for Android (sqlcipher-android). The new SQLCipher for Android API provides major benefits including optimized support for concurrent database access, drastic performance improvements, API simplification, and codebase modernization. Instructions for migrating fromandroid-database-sqlcipher
tosqlcipher-android
may be found here.
Commercial Edition customers with active CipherCare support can still download up-to-date Legacy packages. 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.6.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.6.1', ext: 'aar')
implementation 'androidx.sqlite:sqlite:2.2.0'
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.rawExecSQL(String.format("PRAGMA cipher_license = '%s';", LICENSE_CODE));
}
public void postKey(SQLiteDatabase database) {}
};
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();
SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
public void preKey(SQLiteDatabase database) {
// When using Commercial or Enterprise packages you must call PRAGMA cipher_license with a valid License Code.
// Failure to provide a license code will result in an SQLITE_AUTH(23) error.
// Trial licenses are available at https://www.zetetic.net/sqlcipher/trial/
database.rawExecSQL(String.format("PRAGMA cipher_license = '%s';", "YOUR LICENSE CODE HERE"));
}
public void postKey(SQLiteDatabase database) {}
};
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "test123", null, hook);
database.execSQL("create table t1(a, b)");
database.execSQL("insert into t1(a, b) values(?, ?)", new Object[]{"row 1 column 1", "row 1 column 2"});
}
}
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()
val hook = object : SQLiteDatabaseHook {
override fun preKey(database: SQLiteDatabase) {
// When using Commercial or Enterprise packages you must call PRAGMA cipher_license with a valid License Code.
// Failure to provide a license code will result in an SQLITE_AUTH(23) error.
// Trial licenses are available at https://www.zetetic.net/sqlcipher/trial/
database.rawExecSQL(String.format("PRAGMA cipher_license = '%s';", "YOUR LICENSE CODE HERE"))
}
override fun postKey(database: SQLiteDatabase) {}
}
val database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "test123", null, hook)
database.execSQL("create table t1(a, b)")
database.execSQL("insert into t1(a, b) values(?, ?)",
arrayOf<Any>("row 1 column 1", "row 1 column 2")
)
}
}
The application should now be able to run on either an emulator or device.
Some users have experienced issues on certain devices when loading the native library at runtime. Often times, those experiencing the issue are using Android App Bundles for deployment. Using both the ReLinker tool, in conjunction with SplitInstallHelper has been helpful in resolving the issue for some users. A small example of using ReLinker is below:
Context context = this;
SQLiteDatabase.loadLibs(context, libraries -> {
for (String library : libraries) {
ReLinker.loadLibrary(context, library);
}
});