在Android中通常使用SQLCipher对数据库进行加密。
SQLCipher是一个开源的SQLite加密扩展,支持对db文件进行256位的AES加密。
SQLCipher 官网:
https://www.zetetic.net/sqlcipher/
目前使用的是greenDaoAndroid数据库包,这个包目前是支持SQLCipher加密功能的,而且使用起来相当简单。
首先,在 Android 上添加 SQLCipher 依赖,在 build.gradle 中的 dependencies 里添加
dependencies {
/* 数据库加密框架 */
implementation 'net.zetetic:android-database-sqlcipher:3.5.7@aar'
}
然后,需要在使用时设置一个密码就行了,使用起来没什么难度
// 初始化数据库信息
devOpenHelper = new MyOpenHelper(MyApp.getContext(), DATABASE_NAME, null);
//mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
//数据库加密
mDaoMaster = new DaoMaster(devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD));
就这样,数据库加密就做完了。
但是,这仅仅是对新建数据库来说,如果你之前就有已经在使用中的数据库,那么如果你直接运行项目,就会直接报错,数据库打开失败
Caused by: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master;
这是因为你你之前的数据库没有进行加密,现在是直接使用已加密的方法去使用,这样肯定是不行的。
当然,解决起来也不难,你只需要对已有的数据库进行加密,然后在调用就行了。
对已有数据库进行加密的方法如下:
/**
* 对已有未加密数据库进行加密处理
*/
public class DataBaseEncrypt {
private static volatile DataBaseEncrypt nInstance;
private Boolean isOpen = true;
public DataBaseEncrypt() {
}
public static DataBaseEncrypt getInstences() {
if (nInstance == null) {
synchronized (DataBaseEncrypt.class) {
if (nInstance == null) {
nInstance = new DataBaseEncrypt();
}
}
}
return nInstance;
}
/**
* 如果已有未加密的数据库旧表 先加密已有未加密数据库
*
* @param context
* @param passphrase
*/
public void encrypt(Context context, String dbName, String passphrase) {
File file = context.getDatabasePath(dbName);
if (file.exists() && isOpen) {
try {
//创建临时数据库文件
File newFile = File.createTempFile("sqlcipherutils", "tmp", context.getCacheDir());
//对没有加密的数据库进行加密操作
SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE);
db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';", newFile.getAbsolutePath(), passphrase));
db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
db.rawExecSQL("DETACH DATABASE encrypted;");
int version = db.getVersion();
db.close();
//对已加密过的数据库设置版本号
db = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READWRITE);
db.setVersion(version);
db.close();
file.delete();
//对加密后的数据库进行重新命名
newFile.renameTo(file);
isOpen = false;
} catch (Exception e) {
isOpen = false;
}
}
}
}
然后,在调用时如果能够正常调用直接返回,如果不能说明数据库没有加密过,直接调用加密方法
/**
* 如果对已有数据进行加密,需要先对原有数据库进行加密操作
* @return
*/
public synchronized Database getWritableDatabase() {
try {
return devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD);
} catch (Exception e) {
e.printStackTrace();
//尝试加密后再打开
DataBaseEncrypt.getInstences().encrypt(MyApp.getContext(), DATABASE_NAME, DATABASE_SQLCIPHER_PASSWORD);
return devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD);
}
}
最后,替换一下方法
// 初始化数据库信息
devOpenHelper = new MyOpenHelper(MyApp.getContext(), DATABASE_NAME, null);
//mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
//数据库加密
mDaoMaster = new DaoMaster(getWritableDatabase());
到此,对数据的加密工作基本完成,不管是直接加密还是对已有数据库进行加密都没有问题。