欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android数据库加密

程序员文章站 2022-05-15 14:59:38
...

Android数据库加密

一、简介

SQLite是一个轻量的、跨平台的、开源的数据库引擎,它的读写效率、资源消耗总量、延迟时间和整体简单性上具有的优越性,使其成为移动平台数据库的最佳解决方案(如Android、iOS)。Android系统内置了SQLite数据库,并且提供了一整套的API用于对数据库进行增删改查操作,具体就不详细说明了。

然而,Android平台自带的SQLite有一个致命的缺陷:不支持加密。这就导致存储在SQLite中的数据可以被任何人用任何文本编辑器查看到。如果是普通的数据还好,但是当涉及到一些账号密码,或者聊天内容的时候,我们的应用就会面临严重的安全漏洞隐患。

目前最好且唯一的方案就是SqlCipher对sqlite3整体加密,微信也用的它。开源,且支持很多平台。

二、数据库加密原理

目前主流的数据库都采用了各种安全措施,主要包括用户认证、访问控制、数据加密存储和数据库操作审计等措施。

用户认证:用户或者程序向数据库提供自己的有效身份证明,数据库鉴别用户的身份是否合法,只有合法的用户才能存取数据库中的数据。用户认证是所有安全机制的前提,只有通过认证才能进行授权访问和审计。

访问控制:数据库管理系统为不同的用户分配不同的权限,保证用户只能进行授权的访问。目前,一些大型数据库(如Oracle等)都采用了基于角色的访问控制机制,即为用户授予不同的角色,如db—owner,security administrator 等,不同的角色允许对数据库执行不同的操作。

数据库加密:用户认证以及访问控制对访问数据库进行了控制,但攻击者可能会利用操作系统或数据库漏洞,或物理接触计算机,而直接接触数据库系统文件,从而可能绕过身份认证和存取控制而直接窃取或篡改数据库内容。对数据库中的数据进行加密是防范这类威胁的有效手段。

数据库操作审计:监视和记录用户对数据库所做的各种操作的安全机制,它记录并存储用户的操作,用于事后分析,以检查导致数据库现状的原因以及提供追踪攻击者的线索。数据库的备份与恢复:当数据库发生不可恢复的故障时,可以将数据库恢复到先前的某个一致性的状态。

三、解决方案*

1.将数据加密后再写入数据库:

我们可以对数据的数据库名,表名,列名就行md5,对存储的数据进行加密,例如进行aes加密(Android数据加密之Aes加密),查询的时候再对数据进行解密,这种方式不能说不好,但是使用起来可以想象一下其带来的麻烦程度。

1)优点:

a. 实现数据加密快速,只需添加两个方法
一是:对明文数据进行加密返回密文数据
二是:对密文数据进行解密返回明文数据

b. 程序无需进行太大变动,仅在对数据进行添加,修改,删除,查询时。针对指定的表字段进行修改进行加密,解密的字段即可。

2)不足:
a. 由于对数据进行了加密。所以为了看到明文,必须密文进行解密。因此会增加处理器的消耗。因终端手机的处理能力有限,可能会出现处理数据缓慢的现象发生。

b. 仅仅对数据进行了加密,还是可以看到数据表的sql语句,可能猜测到表的作用。另外,如果没有对一个表中的所有字段加密,则可以看没有加密的明文数据。

这种方式使用简单,在入库/出库只需要将字段做对应的加解密操作即可,一定程度上解决了将数据赤裸裸暴露的问题,这种只是靠存取数据库时通过自己的一些算法加密解密,一定程度上会影响性能。

这种方式并不是彻底的加密,因为数据库的表结构等信息还是能被查看到。另外写入数据库的内容加密后,搜索也是个问题。

2. 对数据库文件加密

将整个数据库整个文件加密,这种方式基本上能解决数据库的信息安全问题。目前已有的SQLite加密基本都是通过这种方式实现的。

目前流行的是一款开源的SQLite加密工具 SQLCipher ,微信也在使用。 SQLCipher是完全开源的,其代码托管在github上。SQLCipher使用256-bit AES加密,由于其基于免费版的SQLite,主要的加密接口和SQLite是相同的,也增加了一些自己的接口。它有一个缺点就是使用该库之后会导致Apk会变大6M左右。下面就是具体介绍SQLCipher的使用方法。

Android数据库加密

SQLCipher使用

SQLCipher是完全开源的软件,提供256-bit AES加密。

SQLCipher是一个在SQLite基础之上进行扩展的开源数据库,SQLCipher具有占地面积小、性能因此它非常适合嵌入式应用的数据库保护,非常适合于移动开发。

整体来说sqlcipher还是比较好用的,封装好了跟正常操作数据库的方式一样,只是在getWritableDatabase()时要多传个password参数。

github地址

导入SQLCipher加密库

implementation 'net.zetetic:android-database-sqlcipher:4.2.0'

替换原生的包

android.database.Cursor 为 net.sqlcipher.Cursor
android.database.sqlite.SQLiteDatabase 为 net.sqlcipher.database.SQLiteDatabase
android.database.SQLiteOpenHelper 为 net.sqlcipher.database.SQLiteOpenHelper

加载SQLCipher所需要的SO库

SQLiteDatabase.loadLibs(this); 

获取读写对象时候附带密码

需要传入一个password,这个password就是用于加密的秘钥

SQLiteOpenHelper.getWritableDatabase("密码"):
SQLiteOpenHelper.getReadableDatabase("密码")

DBCipherHelper

/**
 * Created by : xiaoyehai
 * Create date : 2019/9/12 6:05
 * description :
 * <p>
 * SQLiteOpenHelper要引用sqlcipher包下的
 */
public class DBCipherHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "test_cipher_db";//数据库名字
    public static final String DB_PWD = "whoislcj";//数据库密码
    public static String TABLE_NAME = "person";// 表名
    public static String FIELD_ID = "_id";// 列名
    public static String FIELD_NAME = "name";// 列名
    private static final int DB_VERSION = 1;   // 数据库版本


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

        //不可忽略的 进行so库加载
        SQLiteDatabase.loadLibs(context);
    }


    /**
     * 创建数据库
     *
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建表
        String sql = "CREATE TABLE " + TABLE_NAME + "(" + FIELD_ID + " integer primary key autoincrement , " + FIELD_NAME + " text not null);";
        db.execSQL(sql);

    }

    /**
     * 数据库升级
     *
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

创建一个DBCipherManager数据库管理

Android数据库加密

/**
 * Created by : xiaoyehai
 * Create date : 2019/9/12 6:10
 * description :
 */
public class DBCipherManager {

    private static final String TAG = "DBCipherManager";
    // 静态引用
    private volatile static DBCipherManager mInstance;

    // DatabaseHelper
    private DBCipherHelper dbHelper;

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

    /**
     * 获取单例引用
     *
     * @return
     */
    public static DBCipherManager getInstance(Context context) {
        DBCipherManager inst = mInstance;
        if (inst == null) {
            synchronized (DBCipherManager.class) {
                inst = mInstance;
                if (inst == null) {
                    inst = new DBCipherManager(context);
                    mInstance = inst;
                }
            }
        }
        return inst;
    }

    /**
     * 插入数据:未开启事务
     */
    public void insertData(List<PersonBean> datas) {
        //获取写数据库
        SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
        for (int i = 0; i < datas.size(); i++) {
            //生成要修改或者插入的键值
            ContentValues cv = new ContentValues();
            cv.put(DBCipherHelper.FIELD_NAME, datas.get(i).getName());
            // insert 操作
            db.insert(DBCipherHelper.TABLE_NAME, null, cv);
        }

        //关闭数据库
        db.close();
    }

    /**
     * 插入数据:开启事务批量插入
     */
    public void insertDataByTransaction(List<PersonBean> datas) {
        //获取写数据库
        SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
        db.beginTransaction();  //手动设置开始事务
        try {
            //批量处理操作
            for (int i = 0; i < datas.size(); i++) {
                //生成要修改或者插入的键值
                ContentValues cv = new ContentValues();
                cv.put(DBCipherHelper.FIELD_NAME, datas.get(i).getName());
                // insert 操作
                db.insert(DBCipherHelper.TABLE_NAME, null, cv);
                Log.e(TAG, "insertDatasByTransaction");
            }
            db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            db.endTransaction(); //处理完成
            //关闭数据库
            db.close();
        }
    }

    /**
     * 删除数据
     */
    public void deleteData(String name) {
        //生成条件语句
        StringBuffer whereBuffer = new StringBuffer();
        whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
        //获取写数据库
        SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
        // delete 操作
        db.delete(DBCipherHelper.TABLE_NAME, whereBuffer.toString(), null);
        //关闭数据库
        db.close();
    }

    /**
     * 删除所有数据
     */
    public void deleteAllDatas() {
        SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
        String sql = "delete from " + DBCipherHelper.TABLE_NAME;
        db.execSQL(sql);
        db.close();
    }

    /**
     * 更新数据
     */
    public void updateData(String name, PersonBean personBean) {
        //生成条件语句
        StringBuffer whereBuffer = new StringBuffer();
        whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
        //生成要修改或者插入的键值
        ContentValues cv = new ContentValues();
        cv.put(DBCipherHelper.FIELD_NAME, personBean.getName());
        //获取写数据库
        SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
        // update 操作
        db.update(DBCipherHelper.TABLE_NAME, cv, whereBuffer.toString(), null);
        //关闭数据库
        db.close();
    }

    /**
     * 指定条件查询数据
     */
    public List<PersonBean> queryDatas(String name) {
        List<PersonBean> list = new ArrayList<>();
        //生成条件语句
        StringBuffer whereBuffer = new StringBuffer();
        whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
        //指定要查询的是哪几列数据
        String[] columns = {DBCipherHelper.FIELD_NAME};
        //获取可读数据库
        SQLiteDatabase db = dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
        //查询数据库
        Cursor cursor = null;
        try {
            cursor = db.query(DBCipherHelper.TABLE_NAME, columns, whereBuffer.toString(), null, null, null, null);
            while (cursor.moveToNext()) {
                int count = cursor.getColumnCount();
                String columName = cursor.getColumnName(0);//获取表结构列名
                String tname = cursor.getString(0); //获取表结构列数据
                Log.e(TAG, "count = " + count + " columName = " + columName + "  name =  " + tname);
                PersonBean personBean = new PersonBean(tname);
                list.add(personBean);
            }
            if (cursor != null) {
                cursor.close();
            }
        } catch (SQLException e) {
            Log.e(TAG, "queryDatas" + e.toString());
        }
        //关闭数据库
        db.close();
        return list;
    }

    /**
     * 查询全部数据
     */
    public List<PersonBean> queryAllDatas() {
        List<PersonBean> list = new ArrayList<>();
        //指定要查询的是哪几列数据
        String[] columns = {DBCipherHelper.FIELD_NAME};
        //获取可读数据库
        SQLiteDatabase db = dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
        //查询数据库
        Cursor cursor = null;
        try {
            cursor = db.query(DBCipherHelper.TABLE_NAME, columns, null, null, null, null, null);//获取数据游标
            while (cursor.moveToNext()) {
                int count = cursor.getColumnCount();
                String columeName = cursor.getColumnName(0);//获取表结构列名
                String tname = cursor.getString(0); //获取表结构列数据
                Log.e(TAG, "count = " + count + " columName = " + columeName + "  name =  " + tname);
                PersonBean personBean = new PersonBean(tname);
                list.add(personBean);
            }
            //关闭游标防止内存泄漏
            if (cursor != null) {
                cursor.close();
            }
        } catch (SQLException e) {
            Log.e(TAG, "queryDatas" + e.toString());
        }
        //关闭数据库
        db.close();
        return list;
    }


}

注意:SQLiteDatabase.loadLibs(context);这个千万别忘记调用

使用

  private void queryData() {
        List<PersonBean> list = mDbCipherManager.queryAllDatas(); //查询全部
        //List<PersonBean> list = mDbCipherManager.queryDatas("赵丽颖2"); //根据条件查询
        MyAdapter myAdapter = new MyAdapter(this, list, R.layout.item_list);
        mListView.setAdapter(myAdapter);

    }

    private void updateData() {
        PersonBean personBean = new PersonBean("赵丽颖更新", 100);
        mDbCipherManager.updateData("赵丽颖2",personBean);
    }

    private void deleteData() {
        //mDbCipherManager.deleteData("赵丽颖2"); //根据name删除
        mDbCipherManager.deleteAllDatas(); //删除所有
    }

    private void addData() {
        List<PersonBean> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            PersonBean personBean = new PersonBean("赵丽颖" + i);
            list.add(personBean);
        }

        mDbCipherManager.insertData(list);

    }

Android数据存储之SQLCipher数据库加密