大家好,又见面了,我是你们的朋友全栈君。
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参数。
导入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);
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145501.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...