一 關(guān)于SQlite
Android系統(tǒng)自帶的SQlite是明文存儲(chǔ),不支持加密
二 SQlite加密方式
- 內(nèi)容加密
主要寫入讀取數(shù)據(jù)時(shí)候做加密與解密的動(dòng)作
缺點(diǎn):- 表結(jié)構(gòu)暴露
- 無(wú)法直接搜索
- 數(shù)據(jù)庫(kù)文件加密
對(duì)整個(gè)數(shù)據(jù)庫(kù)文件加密
三 采用開源的SQLCipher進(jìn)行加密
SQLCipher使用256-bit AES加密,由于其基于免費(fèi)版的SQLite,主要的加密接口和SQLite是相同的,但也增加了一些自己的接口
3.1 對(duì)Android原生的數(shù)據(jù)庫(kù)進(jìn)行加密
(1)導(dǎo)入SQLCipher加密庫(kù)
compile "net.zetetic:android-database-sqlcipher:3.5.9@aar"
(2)替換原生的包
* android.database.Cursor 為 net.sqlcipher.Cursor
* android.database.sqlite.SQLiteDatabase 為 net.sqlcipher.database.SQLiteDatabase
* android.database.SQLiteOpenHelper 為 net.sqlcipher.database.SQLiteOpenHelper
(3)加載SQLCipher所需要的SO庫(kù)
SQLiteDatabase.loadLibs(this);
(4)獲取讀寫對(duì)象時(shí)候附帶密碼
SQLiteOpenHelper.getWritableDatabase("密碼"):
SQLiteOpenHelper.getReadableDatabase("密碼")
3.2 對(duì)第三方DbFlow數(shù)據(jù)庫(kù)進(jìn)行加密
(1)替換為DbFlow加密庫(kù)
// sql-cipher database encryption (optional)
compile "com.github.Raizlabs.DBFlow:dbflow-sqlcipher:${dbflow_version}"
compile "net.zetetic:android-database-sqlcipher:${sqlcipher_version}@aar"
(2)更改初始化的方法
FlowManager.init(FlowConfig.builder(this)
.addDatabaseConfig(DatabaseConfig.builder(QiDatabase.class)
.openHelper(new DatabaseConfig.OpenHelperCreator() {
@Override
public OpenHelper createHelper(DatabaseDefinition databaseDefinition, DatabaseHelperListener helperListener) {
return new SQLCipherOpenHelper(databaseDefinition, helperListener) {
@Override
protected String getCipherSecret() {
return "密碼";
}
};
}
})
.build())
.build());
3.3 對(duì)未加密數(shù)據(jù)庫(kù)的轉(zhuǎn)換為加密的數(shù)據(jù)庫(kù)
在加載SQLCipher所需要的SO庫(kù)后及SQLiteDatabase.loadLibs(this)后,調(diào)用以下加密方法
/**
* 對(duì)未加密的數(shù)據(jù)庫(kù)文件做加密處理
*
* @param ctxt 上下文
* @param dbName 數(shù)據(jù)庫(kù)的文件名
* @param passphrase 密碼
* @throws IOException
*/
public static void encrypt(Context ctxt, String dbName, String passphrase) throws IOException {
File originalFile = ctxt.getDatabasePath(dbName);
if (originalFile.exists()) {
File newFile =
File.createTempFile("sqlcipherutils", "tmp",
ctxt.getCacheDir());
SQLiteDatabase db =
SQLiteDatabase.openDatabase(originalFile.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();
originalFile.delete();
newFile.renameTo(originalFile);
}
}
3.4 對(duì)加密數(shù)據(jù)庫(kù)的保存為未加密的數(shù)據(jù)庫(kù)文件
/**
* 將加密數(shù)據(jù)庫(kù)文件保存為非加密的數(shù)據(jù)庫(kù)文件
*
* @param context 上下文
* @param dbName 數(shù)據(jù)庫(kù)名
* @param password 密碼
* @param decFile 待保存的目標(biāo)文件
* @return
*/
public static boolean decrypt(Context context, String dbName, String password, File decFile) {
boolean flag = false;
//先清空目標(biāo)文件
decFile.delete();
try {
File originalFile = context.getDatabasePath(dbName);
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(originalFile, password, null);
if (database.isOpen()) {
database.rawExecSQL(String.format("ATTACH DATABASE '%s' as plaintext KEY '';", decFile.getAbsolutePath()));
database.rawExecSQL("SELECT sqlcipher_export('plaintext');");
database.rawExecSQL("DETACH DATABASE plaintext;");
android.database.sqlite.SQLiteDatabase sqlDB = android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(decFile, null);
if (sqlDB != null)
flag = true;
sqlDB.close();
database.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
// databaseFile.delete();
}