SoFunction
Updated on 2025-03-08

Detailed explanation of Android data storage SQLCipher database encryption

Preface:

Recently, we have studied data sharing between Android Sqlite database and ContentProvider programs. We clearly know that the default storage location of the Sqlite database is in the data/data/pakage/database directory. There is no security for mobile phones that have already ROOT. Once it is used, it will lead to database data leakage. So how can we avoid this? We try to encrypt the database.

Choose the encryption scheme:

1.) The first solution

We can just encrypt the database name, table name, and column name of the data, and encrypt the stored data, such as AES encryption (Android data encryption Aes encryption), and then decrypt the data during query. This method cannot be said to be bad, but when you use it, you can imagine the degree of trouble it brings.

2.) The second solution

Using a third-party encryption open source library, we searched for many Android database encryption solutions, and finally selected the open source framework of SQLCipher. Let’s take a look at how to use SqlCipher.

Introduction to SQLCipher:

SQLCipher is an open source database that extends on the basis of SQLite. SQLCipher has a small footprint and performance, so it is very suitable for database protection for embedded applications and is very suitable for mobile development.

Advantages:

  • High encryption performance and small overhead, only 5-15% of the overhead is used for encryption
  • Completely achieve 100% encryption of the database
  • Adopt good encryption method (CBC encryption mode)
  • Easy to use and achieve application-level encryption
  • Using the algorithm provided by the OpenSSL encryption library

How to use SQLCipher:

1.) Add the following code to the article, the latest version 3.4.0 is currently used

dependencies {

  compile ':android-database-sqlcipher:3.4.0'

}

2.) Create a SQLiteOpenHelper Note the following class references to all Sqlite-related classes

import ;
import ;
import ;
import ;
import ;


public class DBCipherHelper extends SQLiteOpenHelper {
  private static final String TAG = "DatabaseHelper";
  private static final String DB_NAME = "test_cipher_db";//Database name  public static final String DB_PWD="whoislcj";//Database Password  public static String TABLE_NAME = "person";// Table name  public static String FIELD_ID = "id";// Column name  public static String FIELD_NAME= "name";// Column name  private static final int DB_VERSION = 1;  // Database version
  public DBCipherHelper(Context context, String name,  factory, int version) {
    super(context, name, factory, version);
    //Not negligible to load the so library    (context);
  }

  public DBCipherHelper(Context context) {
    this(context, DB_NAME, null, DB_VERSION);
  }

  /**
    * Create a database
    * @param db
    */
  @Override
  public void onCreate(SQLiteDatabase db) {
    //Create a table    createTable(db);
  }

  private void createTable(SQLiteDatabase db){
    String sql = "CREATE TABLE " + TABLE_NAME + "(" + FIELD_ID + " integer primary key autoincrement , " + FIELD_NAME + " text not null);";
    try {
      (sql);
    } catch (SQLException e) {
      (TAG, "onCreate " + TABLE_NAME + "Error" + ());
      return;
    }
  }

  /**
    * Database upgrade
    * @param db
    * @param oldVersion
    * @param newVersion
    */
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  }

}

Note: (context); Don't forget to call this

3.) Create a DBCipherManager database management

The specific implementation of traditional SQLiteOpenHelper is exactly the same, and the different places are where the database handle is obtained

Traditional way:

    //Get the writable database    SQLiteDatabase db = ();

    //Get readable database    SQLiteDatabase db = (); 

The current way: you need to pass in a password, which is the secret key used for encryption

     //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    //Get readable database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);

Next is the specific implementation:

 import ;
import ;
import ;
import ;
import ;
import ;


/**
  * Database Manager - Provide database encapsulation
  *
  */
public class DBCipherManager {
  private static final String TAG = "DatabaseManager";
  // Static reference  private volatile static DBCipherManager mInstance;
  // DatabaseHelper
  private DBCipherHelper dbHelper;

  private DBCipherManager(Context context) {
    dbHelper = new DBCipherHelper(());
  }

  /**
    * Get singleton reference
    *
    * @param context
    * @return
    */
  public static DBCipherManager getInstance(Context context) {
    DBCipherManager inst = mInstance;
    if (inst == null) {
      synchronized () {
        inst = mInstance;
        if (inst == null) {
          inst = new DBCipherManager(context);
          mInstance = inst;
        }
      }
    }
    return inst;
  }

  /**
    * Insert data
    */
  public void insertData(String name) {
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    //Generate key values ​​to be modified or inserted    ContentValues cv = new ContentValues();
    (DBCipherHelper.FIELD_NAME, name);
    // insert operation    (DBCipherHelper.TABLE_NAME, null, cv);
    //Close the database    ();
  }

  /**
    * Transaction batch insertion is not enabled
    * @param testCount
    */
  public void insertDatasByNomarl(int testCount){
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    for(int i =0;i<testCount;i++ ){
      //Generate key values ​​to be modified or inserted      ContentValues cv = new ContentValues();
      (DBCipherHelper.FIELD_NAME, (i));
      // insert operation      (DBCipherHelper.TABLE_NAME, null, cv);
      (TAG, "insertDatasByNomarl");
    }
    //Close the database    ();
  }

  /**
    * Test enable transaction batch insertion
    * @param testCount
    */
  public void insertDatasByTransaction(int testCount){
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    (); // Set up the start transaction manually    try{
      //Batch processing operation      for(int i =0;i<testCount;i++ ){
        //Generate key values ​​to be modified or inserted        ContentValues cv = new ContentValues();
        (DBCipherHelper.FIELD_NAME, (i));
        // insert operation        (DBCipherHelper.TABLE_NAME, null, cv);
        (TAG, "insertDatasByTransaction");
      }
      (); //The transaction processing is successfully set, if it is not set, it will automatically roll back and not commit    }catch(Exception e){

    }finally{
      (); // Processing is completed      //Close the database      ();
    }
  }

  /**
    * Delete data
    */
  public void deleteData(String name) {
    //Generate conditional statement    StringBuffer whereBuffer = new StringBuffer();
    (DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    // delete operation    (DBCipherHelper.TABLE_NAME, (), null);
    //Close the database    ();
  }

  /**
    * Delete all data
    */
  public void deleteDatas()
  {
    String sql="delete from "+ DBCipherHelper.TABLE_NAME;
    execSQL(sql);
  }

  /**
    * Update data
    */
  public void updateData(String name) {
    //Generate conditional statement    StringBuffer whereBuffer = new StringBuffer();
    (DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
    //Generate key values ​​to be modified or inserted    ContentValues cv = new ContentValues();
    (DBCipherHelper.FIELD_NAME, name+name);
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    // update operation    (DBCipherHelper.TABLE_NAME, cv, (), null);
    //Close the database    ();
  }

  /**
    * Specify the conditions to query data
    */
  public void queryDatas(String name){
    //Generate conditional statement    StringBuffer whereBuffer = new StringBuffer();
    (DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
    //Specify which columns of data to be queried    String[] columns = {DBCipherHelper.FIELD_NAME};
    //Get readable database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    //Query the database    Cursor cursor = null;
    try {
      cursor = (DBCipherHelper.TABLE_NAME, columns, (), null, null, null, null);
      while (()) {
        int count = ();
        String columName = (0);
        String tname = (0);
        (TAG, "count = " + count + " columName = " + columName + " name = " +tname);
      }
      if (cursor != null) {
        ();
      }
    } catch (SQLException e) {
      (TAG, "queryDatas" + ());
    }
    //Close the database    ();
  }

  /**
    * Query all data
    */
  public void queryDatas(){
    //Specify which columns of data to be queried    String[] columns = {DBCipherHelper.FIELD_NAME};
    //Get readable database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    //Query the database    Cursor cursor = null;
    try {
      cursor = (DBCipherHelper.TABLE_NAME, columns, null, null, null, null, null);//Get data cursor      while (()) {
        int count = ();
        String columeName = (0);//Get the table structure column name        String name = (0);//Get table structure column data        (TAG, "count = " + count + " columName = " + columeName + " name = " +name);
      }
      //Close cursor to prevent memory leak      if (cursor != null) {
        ();
      }
    } catch (SQLException e) {
      (TAG, "queryDatas" + ());
    }
    //Close the database    ();
  }

  /**
    * Execute SQL statements
    */
  private void execSQL(String sql){
    //Get the write database    SQLiteDatabase db = (DBCipherHelper.DB_PWD);
    //Execute the SQL statement directly    (sql);//or    //Close the database    ();
  }

}

4.) How to call it

//Clear data        ().deleteDatas();
        //Insert data        for (int i = 0; i < 10; i++) {
          ().insertData((i));
        }
        //Delete data        ().deleteData((5));
        //Update data        ().updateData((3));
        //Query data        ().queryDatas();

5.) Transaction support is the same as traditional

//Get the write database    SQLiteDatabase db = ();
    (); // Set up the start transaction manually    try{
      // Handle batch operations here      for(int i =0;i<testCount;i++ ){
        //Generate key values ​​to be modified or inserted        ContentValues cv = new ContentValues();
        (DBHelper.FIELD_NAME, (i));
        // insert operation        (DBHelper.TABLE_NAME, null, cv);
      }
      (); //The transaction processing is successfully set, if it is not set, it will automatically roll back and not commit    }catch(Exception e){

    }finally{
      (); // Processing is completed      //Close the database      ();
    }


Summarize:

The SQLCipher usage summary ends here.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.