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.

17 thoughts on “Tutorial: Add SQLCipher to your Android app”

  1. Really thank you for sharing such a descriptive document.
    And it becomes much easier to implement SqlCipher.
    Thanks a ton 🙂

  2. Dear Antonina, thank you for the article! It is really helpful. Can you please suggest, from your experience, what is the best way to generate and store the password for opening SQLCipher?

    1. Thank you for nice words 🙂

      Generating and storing SQLCipher password is everybody’s issue. The way how you can do it can depends on Android app itself. For instance, if user enters some data, he or she can enter a passphrase (or any other bit of information), and you can use it for SQLCipher password generation. Even without user’s input, you can take some id from server (per user) and use it in conjunction with something else. Don’t store the password anywhere in the generated package, especially hardcoded in Java file. Don’t store it permanently at all, as getting access to it ruins the whole point of encryption 😉

      For more information, check sjlombardo’s comment here – https://discuss.zetetic.net/t/sqlcipher-how-to-protect-the-key/522

  3. Hi Antonina.
    Thanks you for the great article!

    I have a few questions.
    1. For obvious reasons, I can not store passphrase in the application.
    How then can I realize the ability log on with fingerprint?
    2. I do not quite understand, if I create two versions of the application, paid and free – I can use a paid version with a “SQLCipher Community Edition License”? Or do I need to purchase a license for commercial use – “SQLCipher Commercial Edition License”?

    1. Hello Vadim,

      Thank you for your comment and questions 😉 I’m sorry for answering this late, hope you’re still interested in the response.

      1. I honestly don’t know. Haven’t played with the fingerprint. Here – https://developer.android.com/samples/ConfirmCredential/src/com.example.android.confirmcredential/MainActivity.html and here – https://developer.android.com/samples/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.html#l98 you can find some sample code, perhaps it help.

      2. From what I understand, there isn’t a division to free and paid apps, rather open and closed source projects. So, you can go with a Community Edition BUT remember to include their license and copyright text, as stated here – https://www.zetetic.net/sqlcipher/open-source/ . Commercial Edition is per user per platform. It’s main benefit is the high quality fast support of SqlCipher team – instructions, code verification and quick responses. So in case of a really big and complex commercial project I would go with a Commercial Edition, otherwise it might be too expensive comparing to the cost of development.

      In both cases, you can create a discussion here – https://discuss.zetetic.net/c/sqlcipher in order to get answers to your questions 🙂

      Thanks and best of luck!

  4. What libs you need to use sqlcipher?, I still have a compilation error
    java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/development.alberto.com.sqlcipherexample-1/base.apk”, zip couldn’t find “libstlport_shared.so”

  5. Failed to find this solution before 6 months. Tried again today in free time. TBH its like present for me 😀
    This is awesome tutorial . Thank you very much for sharing.. World Needs More of your kind of people 🙂

Leave a Reply

Your email address will not be published.