详解Android数据存储之SQLCipher数据库加密
前言:
最近研究了AndroidSqlite数据库以及ContentProvider程序间数据共享,我们清晰的知道Sqlite数据库默认存放位置data/data/pakage/database目录下,对于已经ROOT的手机来说的没有任何安全性可以,一旦被利用将会导致数据库数据的泄漏,所以我们该如何避免这种事情的发生呢?我们尝试这对数据库进行加密。
选择加密方案:
1.)第一种方案
我们可以对数据的数据库名,表名,列名就行md5,对存储的数据进行加密,例如进行aes加密(Android数据加密之Aes加密),查询的时候再对数据进行解密,这种方式不能说不好,但是使用起来可以想象一下其带来的麻烦程度。
2.)第二种方案
采用第三方加密开源库,查找了很多种Android数据库加密方案,最终选定SQLCipher这个开源框架,接下来看下SqlCipher如何使用。
SQLCipher简介:
SQLCipher是一个在SQLite基础之上进行扩展的开源数据库,SQLCipher具有占地面积小、性能因此它非常适合嵌入式应用的数据库保护,非常适合于移动开发。
优势:
- 加密性能高、开销小,只要5-15%的开销用于加密
- 完全做到数据库100%加密
- 采用良好的加密方式(CBC加密模式)
- 使用方便,做到应用级别加密
- 采用OpenSSL加密库提供的算法
SQLCipher使用方式:
1.)在build.gradle文中添加如下代码,当前使用的是最新版本3.4.0
dependencies{
compile'net.zetetic:android-database-sqlcipher:3.4.0'
}
2.)创建一个SQLiteOpenHelper注意接下来所以有关Sqlite相关类全部引用net.sqlcipher.database的类
importandroid.content.Context;
importandroid.util.Log;
importnet.sqlcipher.SQLException;
importnet.sqlcipher.database.SQLiteDatabase;
importnet.sqlcipher.database.SQLiteOpenHelper;
publicclassDBCipherHelperextendsSQLiteOpenHelper{
privatestaticfinalStringTAG="DatabaseHelper";
privatestaticfinalStringDB_NAME="test_cipher_db";//数据库名字
publicstaticfinalStringDB_PWD="whoislcj";//数据库密码
publicstaticStringTABLE_NAME="person";//表名
publicstaticStringFIELD_ID="id";//列名
publicstaticStringFIELD_NAME="name";//列名
privatestaticfinalintDB_VERSION=1;//数据库版本
publicDBCipherHelper(Contextcontext,Stringname,SQLiteDatabase.CursorFactoryfactory,intversion){
super(context,name,factory,version);
//不可忽略的进行so库加载
SQLiteDatabase.loadLibs(context);
}
publicDBCipherHelper(Contextcontext){
this(context,DB_NAME,null,DB_VERSION);
}
/**
*创建数据库
*@paramdb
*/
@Override
publicvoidonCreate(SQLiteDatabasedb){
//创建表
createTable(db);
}
privatevoidcreateTable(SQLiteDatabasedb){
Stringsql="CREATETABLE"+TABLE_NAME+"("+FIELD_ID+"integerprimarykeyautoincrement,"+FIELD_NAME+"textnotnull);";
try{
db.execSQL(sql);
}catch(SQLExceptione){
Log.e(TAG,"onCreate"+TABLE_NAME+"Error"+e.toString());
return;
}
}
/**
*数据库升级
*@paramdb
*@paramoldVersion
*@paramnewVersion
*/
@Override
publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
}
}
注意:SQLiteDatabase.loadLibs(context);这个千万别忘记调用
3.)创建一个DBCipherManager数据库管理
具体实现传统的SQLiteOpenHelper都是完全相同的,不同的地方在获取数据库句柄的地方
传统方式:
//获取可写数据库 SQLiteDatabasedb=dbHelper.getWritableDatabase(); //获取可读数据库 SQLiteDatabasedb=dbHelper.getReadableDatabase();
现在的方式:需要传入一个password,这个password就是用于加密的秘钥
//获取写数据库 SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD); //获取可读数据库 SQLiteDatabasedb=dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
接下来就是具体实现:
importandroid.content.ContentValues;
importandroid.content.Context;
importandroid.util.Log;
importnet.sqlcipher.Cursor;
importnet.sqlcipher.SQLException;
importnet.sqlcipher.database.SQLiteDatabase;
/**
*数据库管理者-提供数据库封装
*
*/
publicclassDBCipherManager{
privatestaticfinalStringTAG="DatabaseManager";
//静态引用
privatevolatilestaticDBCipherManagermInstance;
//DatabaseHelper
privateDBCipherHelperdbHelper;
privateDBCipherManager(Contextcontext){
dbHelper=newDBCipherHelper(context.getApplicationContext());
}
/**
*获取单例引用
*
*@paramcontext
*@return
*/
publicstaticDBCipherManagergetInstance(Contextcontext){
DBCipherManagerinst=mInstance;
if(inst==null){
synchronized(DBCipherManager.class){
inst=mInstance;
if(inst==null){
inst=newDBCipherManager(context);
mInstance=inst;
}
}
}
returninst;
}
/**
*插入数据
*/
publicvoidinsertData(Stringname){
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
//生成要修改或者插入的键值
ContentValuescv=newContentValues();
cv.put(DBCipherHelper.FIELD_NAME,name);
//insert操作
db.insert(DBCipherHelper.TABLE_NAME,null,cv);
//关闭数据库
db.close();
}
/**
*未开启事务批量插入
*@paramtestCount
*/
publicvoidinsertDatasByNomarl(inttestCount){
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
for(inti=0;i<testCount;i++){
//生成要修改或者插入的键值
ContentValuescv=newContentValues();
cv.put(DBCipherHelper.FIELD_NAME,String.valueOf(i));
//insert操作
db.insert(DBCipherHelper.TABLE_NAME,null,cv);
Log.e(TAG,"insertDatasByNomarl");
}
//关闭数据库
db.close();
}
/**
*测试开启事务批量插入
*@paramtestCount
*/
publicvoidinsertDatasByTransaction(inttestCount){
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
db.beginTransaction();//手动设置开始事务
try{
//批量处理操作
for(inti=0;i<testCount;i++){
//生成要修改或者插入的键值
ContentValuescv=newContentValues();
cv.put(DBCipherHelper.FIELD_NAME,String.valueOf(i));
//insert操作
db.insert(DBCipherHelper.TABLE_NAME,null,cv);
Log.e(TAG,"insertDatasByTransaction");
}
db.setTransactionSuccessful();//设置事务处理成功,不设置会自动回滚不提交
}catch(Exceptione){
}finally{
db.endTransaction();//处理完成
//关闭数据库
db.close();
}
}
/**
*删除数据
*/
publicvoiddeleteData(Stringname){
//生成条件语句
StringBufferwhereBuffer=newStringBuffer();
whereBuffer.append(DBCipherHelper.FIELD_NAME).append("=").append("'").append(name).append("'");
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
//delete操作
db.delete(DBCipherHelper.TABLE_NAME,whereBuffer.toString(),null);
//关闭数据库
db.close();
}
/**
*删除所有数据
*/
publicvoiddeleteDatas()
{
Stringsql="deletefrom"+DBCipherHelper.TABLE_NAME;
execSQL(sql);
}
/**
*更新数据
*/
publicvoidupdateData(Stringname){
//生成条件语句
StringBufferwhereBuffer=newStringBuffer();
whereBuffer.append(DBCipherHelper.FIELD_NAME).append("=").append("'").append(name).append("'");
//生成要修改或者插入的键值
ContentValuescv=newContentValues();
cv.put(DBCipherHelper.FIELD_NAME,name+name);
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
//update操作
db.update(DBCipherHelper.TABLE_NAME,cv,whereBuffer.toString(),null);
//关闭数据库
db.close();
}
/**
*指定条件查询数据
*/
publicvoidqueryDatas(Stringname){
//生成条件语句
StringBufferwhereBuffer=newStringBuffer();
whereBuffer.append(DBCipherHelper.FIELD_NAME).append("=").append("'").append(name).append("'");
//指定要查询的是哪几列数据
String[]columns={DBCipherHelper.FIELD_NAME};
//获取可读数据库
SQLiteDatabasedb=dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
//查询数据库
Cursorcursor=null;
try{
cursor=db.query(DBCipherHelper.TABLE_NAME,columns,whereBuffer.toString(),null,null,null,null);
while(cursor.moveToNext()){
intcount=cursor.getColumnCount();
StringcolumName=cursor.getColumnName(0);
Stringtname=cursor.getString(0);
Log.e(TAG,"count="+count+"columName="+columName+"name="+tname);
}
if(cursor!=null){
cursor.close();
}
}catch(SQLExceptione){
Log.e(TAG,"queryDatas"+e.toString());
}
//关闭数据库
db.close();
}
/**
*查询全部数据
*/
publicvoidqueryDatas(){
//指定要查询的是哪几列数据
String[]columns={DBCipherHelper.FIELD_NAME};
//获取可读数据库
SQLiteDatabasedb=dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
//查询数据库
Cursorcursor=null;
try{
cursor=db.query(DBCipherHelper.TABLE_NAME,columns,null,null,null,null,null);//获取数据游标
while(cursor.moveToNext()){
intcount=cursor.getColumnCount();
StringcolumeName=cursor.getColumnName(0);//获取表结构列名
Stringname=cursor.getString(0);//获取表结构列数据
Log.e(TAG,"count="+count+"columName="+columeName+"name="+name);
}
//关闭游标防止内存泄漏
if(cursor!=null){
cursor.close();
}
}catch(SQLExceptione){
Log.e(TAG,"queryDatas"+e.toString());
}
//关闭数据库
db.close();
}
/**
*执行sql语句
*/
privatevoidexecSQL(Stringsql){
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
//直接执行sql语句
db.execSQL(sql);//或者
//关闭数据库
db.close();
}
}
4.)具体怎么调用
//清空数据
DBCipherManager.getInstance(MainActivity.this).deleteDatas();
//插入数据
for(inti=0;i<10;i++){
DBCipherManager.getInstance(MainActivity.this).insertData(String.valueOf(i));
}
//删除数据
DBCipherManager.getInstance(MainActivity.this).deleteData(String.valueOf(5));
//更新数据
DBCipherManager.getInstance(MainActivity.this).updateData(String.valueOf(3));
//查询数据
DBCipherManager.getInstance(MainActivity.this).queryDatas();
5.)事务支持和传统方式一样
//获取写数据库
SQLiteDatabasedb=dbHelper.getWritableDatabase();
db.beginTransaction();//手动设置开始事务
try{
//在此处理批量操作
for(inti=0;i<testCount;i++){
//生成要修改或者插入的键值
ContentValuescv=newContentValues();
cv.put(DBHelper.FIELD_NAME,String.valueOf(i));
//insert操作
db.insert(DBHelper.TABLE_NAME,null,cv);
}
db.setTransactionSuccessful();//设置事务处理成功,不设置会自动回滚不提交
}catch(Exceptione){
}finally{
db.endTransaction();//处理完成
//关闭数据库
db.close();
}
总结:
SQLCipher使用总结到此结束。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。