Tutorial: Add SQLCipher to your Android app

Hi guys!

Today’s tutorial will be about SQLCipher and its integration in Android (with the means of Android Studio). Just to give some introduction, SQLCipher is used for a full database encryption. What full means is that the DB file is entirely encrypted, not only separate rows or tables. Zetetic, the company behind SQLCipher, provides Community and Commercial edition. You can use the former for commercial projects, though, just have to provide license doc in your app. Also, SQLCipher is not for Android only, it can be used on various platforms.

Regarding security features, SQLCipher uses 256-bit AES in CBC mode by default, with 1kB page size, and it derives the key data using PBKDF2 function. The difference in API that you instantly notice is the passphrase argument in getReadableDatabase() and getWritableDatabase() methods. Now, this passphrase should never be stored in the application, otherwise the whole point of encryption is wasted. One thing to keep in mind, is that the passphrase is not a key! Yes, the DB is encrypted using the key, but it is generated based on the passphrase (~ password) and also can be re-set using PRAGMA rekey command.

You can modify most of the settings using special pragmas.

Unfortunately, on the website, they only have Eclipse integration doc, which I find deprecated in terms of Android, so it might be confusing as to where to start. There is, however, a thread on how to add SQLCipher to AS project, but it tells about the jar files and manual adding of libraries, and it’s not an up-to date too. But hey, this tutorial aims to fix this! 😉

First of all, you need a new (or existing) Android Studio project. I have created one with the below settings (and others were left default): SQLCipher project settings

I also like to have the required things only, so I removed all the boilerplate code and resources (floating button, menu, etc.), but you may leave it.

In order to attach SQLCipher, just add compile 'net.zetetic:android-database-sqlcipher:3.3.1-2@aar' to build.gradle dependencies, so it looks like this:

SQLCipher Gradle dependency

You can always check the latest version of aar dependency here. Also, there’s a javadoc, which I find quite useful.

Even before using SQLCipher features, you can re-sync and run the app in order to test how the APK size has changed. In my case it’s 34.0 KB to 7.00 MB change which is kinda huge, right? Yes, but I’ll explain later why 😉

Now, that we have a dependency, we can proceed with the DB creation. Obviously, we need an SQLite database now, and I have added some code to the example project to make this happen (hugely inspired by Android Developers Saving Data training 😉

First, I’ve created FeedReaderContract class with some table info, as below:

    package com.example.sqlcipher;

    import android.provider.BaseColumns;

    public final class FeedReaderContract {
        public FeedReaderContract() {}

        /* Inner class that defines the table contents */
        public static abstract class FeedEntry implements BaseColumns {
            public static final String TABLE_NAME = "news";
            public static final String COLUMN_NAME_ENTRY_ID = "news_id";
            public static final String COLUMN_NAME_TITLE = "title";
            public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        }
    }

Added a FeedReaderDbHelper class, which looks pretty standard:

    package com.example.sqlcipher;

    import android.content.Context;

    import net.sqlcipher.database.SQLiteDatabase;
    import net.sqlcipher.database.SQLiteOpenHelper;

    public class FeedReaderDbHelper extends SQLiteOpenHelper {
        private static FeedReaderDbHelper instance;

        public static final int DATABASE_VERSION = 1;
        public static final String DATABASE_NAME = "FeedReader.db";

        private static final String TEXT_TYPE = " TEXT";
        private static final String SQL_CREATE_ENTRIES =
                "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
                        FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
                        FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + "," +
                        FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + "," +
                        FeedReaderContract.FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE +
                        " )";

        private static final String SQL_DELETE_ENTRIES =
                "DROP TABLE IF EXISTS " + FeedReaderContract.FeedEntry.TABLE_NAME;

        public FeedReaderDbHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        static public synchronized FeedReaderDbHelper getInstance(Context context) {
            if (instance == null) {
                instance = new FeedReaderDbHelper(context);
            }
            return instance;
        }

        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE_ENTRIES);
        }
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL(SQL_DELETE_ENTRIES);
            onCreate(db);
        }
    }

Although, there’s nothing really special about this helper, please notice a few things. First of all, the SQLiteDatabase and SQLiteOpenHelper are taken from net.sqlcipher.database, not Android’s android.database.sqlite package. This is because SQLCipher uses its own implementation of SQLite 🙂 Second thing is the absence of onDowngrade() method. The reason behind this is simple – SQLCipher supports Android 2.1+ and in order to maintain the compatibility through this and newer Android versions, they’ve decided to not have onDowngrade() at all, as it was added in API 11. Here is the link to the official statement on this.

OK, so the next thing would be to use the FeedReaderDbHelper, so let’s create insertSthToDb() method in our MainActivity class, and add the following line to it: SQLiteDatabase db = FeedReaderDbHelper.getInstance(this).getWritableDatabase("somePass");. If you try to run the app now, you will get a runtime exception – java.lang.UnsatisfiedLinkError: No implementation found for void net.sqlcipher.database.SQLiteDatabase.dbopen(java.lang.String, int) (tried Java_net_sqlcipher_database_SQLiteDatabase_dbopen and Java_net_sqlcipher_database_SQLiteDatabase_dbopen__Ljava_lang_String_2I) and it’s because we need to load a few native libraries first. So, add this line to the onCreate() method: SQLiteDatabase.loadLibs(this);. Do Ctrl+Click on this method and you’ll see what it does is loading stlport_shared, sqlcipher_android, and database_sqlcipher libs plus the icudt46l.dat file. So, now the mystery of 7MB APK size increase is solved! 😉

Below, I insert one row to the DB and later do the SELECT query in order to see if cursor returns one item. If it doest, – great, this means our DB does what it should – saves the data. But, it doesn’t prove that the data is encrypted. What you can do to make sure the DB file is really encrypted, is to close and open the DB connection with an empty password. When running the app, you’ll get a net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: create locale table failed runtime exception.

Also, check a DB file on the file system. Just run the following command from Android Studio’s terminal: adb shell "run-as com.example.sqlcipher cp /data/user/0/com.example.sqlcipher/databases/FeedReader.db /sdcard/FeedReader.db".This will copy the DB from app’s databases directory to SDCARD. Then you can easily copy and open it either on your PC or in some SQLite viewer app, like aSQLiteManager. In latter, you should get an error message saying that the .db file cannot be recognized! Similarly, if you don’t want to have an encryption just yet, – use an empty password from the very beginning.

Yey, so our DB is encrypted now, and the MainActivity looks like this:

    package com.example.sqlcipher;

    import android.app.Activity;
    import android.content.ContentValues;
    import android.os.Bundle;
    import android.util.Log;
    import net.sqlcipher.Cursor;
    import net.sqlcipher.database.SQLiteDatabase;

    public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SQLiteDatabase.loadLibs(this);
        insertSthToDb();
    }

    private void insertSthToDb() {
        SQLiteDatabase db = FeedReaderDbHelper.getInstance(this).getWritableDatabase("somePass");

        ContentValues values = new ContentValues();
        values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, 1);
        values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, "Easter Bunny has escaped!");
        values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_SUBTITLE, "A thrilling story which proves how fragile our hearts are...");

        db.insert(FeedReaderContract.FeedEntry.TABLE_NAME, null, values);

        Cursor cursor = db.rawQuery("SELECT * FROM '" + FeedReaderContract.FeedEntry.TABLE_NAME + "';", null);
        Log.d(MainActivity.class.getSimpleName(), "Rows count: " + cursor.getCount());
        cursor.close();
        db.close();

        // this will throw net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: create locale table failed
        //db = FeedReaderDbHelper.getInstance(this).getWritableDatabase("");
    }
}

That’s all I wanted to share with you today 😉 As always, I hope you have enjoyed this tutorial and don’t hesitate to leave a comment below! 😉


Other useful links:

Discussions on issues, updates, FAQs
https://discuss.zetetic.net/c/sqlcipher

android-database-sqlcipher GitHub project
https://github.com/sqlcipher/android-database-sqlcipher

sqlcipher-android-tests GitHub project
https://github.com/sqlcipher/sqlcipher-android-tests

SQLCipher performance tips
https://discuss.zetetic.net/t/sqlcipher-performance/14

How to change key for encryption
https://groups.google.com/forum/#!topic/sqlcipher/2o-Imwdevuo

Detailed tutorial on DB encryption
https://sunny89blog.wordpress.com/2017/05/14/securing-android-sqlite-db-with-sqlcipher-and-jni/

Like and share:

Published by

Tonia Tkachuk

I’m an Android Developer, writing code for living and for fun. Love beautiful apps with a clean code inside. Enjoy travelling and reading.