Android数据库加密

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

大家好,又见面了,我是你们的朋友全栈君。

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的使用方法。

在这里插入图片描述

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数据库管理

在这里插入图片描述

/** * 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数据库加密

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145501.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号