前言
應用使用 SQLite 來存儲數據,很多時候需要對一部分的數據進行加密。常見的做法是對要存儲的內容加密后存到數據庫中,使用的時候對數據進行解密。這樣就會有大量的性能消耗在數據的加密解密上。
SQLite 本身是支持加密功能的 (免費版本不提供加密功能,商業版本是支持加密模塊)。SQLCipher 是一個開源的 SQLite 加密的擴展,支持對 db 文件進行 256位的 AES 加密。
加密與非加密的數據庫對比
打開 Terminal 輸入以下內容,
~ $ sqlite3 sqlcipher.db sqlite> PRAGMA KEY=’test123′; sqlite> CREATE TABLE t1(a,b); sqlite> INSERT INTO t1(a,b) VALUES (‘one for the money’, ‘two for the show’); sqlite> .quit ~$ hexdump -C sqlite.db
結果:

配置步驟
1、到 github 上下載 SQLCipher 插件,並存放到項目根目錄下。

2、sqlcipher.xcodeproj 以 static library 的方式添加到項目里面。


3、關聯新添加的靜態庫 (注意,這里不能包含系統的 libsqlite3.dylib)

4、設置 Build Setting
"Header Search Path" 添加,"../sqlcipher/src",這里需要注意路徑的關系。

"Other C Flags" 添加 "-DSQLITE_HAS_CODEC"

由於 SQLCipher 是支持 Mac OSX, iOS, WatchOS, TVOS 等多個平台,所以必要的時候,需要修改 sqlcipher.project 的配置文件,否則會引起編譯或者 linking 錯誤,修改如下:

項目中使用示例
#import <sqlite3.h>
- (void)openDB2 {
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *db2Path = [documentPath stringByAppendingPathComponent:db2Name];
if (sqlite3_open([db2Path UTF8String], &database2) == SQLITE_OK) {
const char* key = [@"eileen" UTF8String];
sqlite3_key(database2, key, strlen(key));
// if (sqlite3_exec(database2, (const char*) "CREATE TABLE t1 (a, b);", NULL, NULL, NULL) == SQLITE_OK) {
// NSLog(@"password is correct, or, database has been initializ");
//
// } else {
// NSLog(@"incorrect password!");
// }
// sqlite3_close(database2);
if (sqlite3_exec(database2, "INSERT INTO t1(a, b) VALUES('qqqqqqq', 'pppppp')", NULL, NULL, NULL)==SQLITE_OK) {
NSLog(@"密碼正確");
}
else {
NSLog(@"密碼錯誤");
}
sqlite3_stmt *statement = NULL;
sqlite3_prepare_v2(database2, "SELECT a,b FROM t1", -1, &statement, NULL);
while (sqlite3_step(statement) == SQLITE_ROW) {
char *field0 = (char*)sqlite3_column_text(statement, 0);
NSString *field0Str = @"";
if (field0) {
field0Str = [NSString stringWithUTF8String:field0];
}
char *field1 = (char*)sqlite3_column_text(statement, 1);
NSString *field1Str = @"";
if (field1) {
field1Str = [NSString stringWithUTF8String:field1];
}
NSLog(@"a = %@, b = %@;", field0Str, field1Str);
}
sqlite3_finalize(statement);
}
else {
sqlite3_close(database2);
}
}
Terminal 上安裝 SQLCipher
總的來說在 Terminal 執行以下 2句即可,參照:
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="-lcrypto" ;# Run the configure script $ make ;# Run the makefile.
1、cd 到下載好的 sqlcipher 目錄下,並執行
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="-lcrypto"

2、輸入
$ make
2.1、發生了錯誤,
sqlite3.c:18280:10: fatal error: 'openssl/rand.h' file not found
#include <openssl/rand.h>
見下圖:

解決方法,輸入:
$ brew link openssl --force
2.2、發生錯誤,“-bash: brew: command not found”,證明 OS 尚未安裝 Homebrew。(安裝 Homebrew 的前提下是安裝了 Xcode, 並且 Command Line Tools 已安裝, Terminal 輸入 "gcc --version" 檢查)
解決方法,輸入:
$ -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2.3、安裝好 Homebrew 后,重新執行
$ brew link openssl --force
發生了錯誤:Error: No such keg: /usr/local/Cellar/openssl
解決方法,使用 brew 安裝 openssl,輸入:
$ brew install openssl

2.4、安裝完 openssl 后,重新執行
$ brew link openssl --force

執行完后,再重新執行
$ make

多次執行 make 操作會發生錯誤 “make: Nothing to be done for `all'”,解決方法,輸入:
$ make clean // 重新執行 $ make
3、執行完前面 2 步,sqlcipher 目錄下會多了一個 sqlcipher 文件,用於 Terminal 中管理數據庫。
Terminal 查看和修改數據庫的密碼管理
cd 到剛才新生成的 sqlcipher 文件的目錄下,執行以下的操作,參照。
1、使用 SQLCipher 加密已經存在的數據庫
$ ./sqlcipher plaintext.db
sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';
sqlite> SELECT sqlcipher_export('encrypted');
sqlite> DETACH DATABASE encrypted;
2、解除使用 SQLCipher 加密的數據庫密碼
$ ./sqlcipher encrypted.db
sqlite> PRAGMA key = 'testkey';
sqlite> ATTACH DATABASE 'plaintext.db' AS plaintext KEY ''; -- empty key will disable encryption
sqlite> SELECT sqlcipher_export('plaintext');
sqlite> DETACH DATABASE plaintext;
注意
有些軟件的加密方式是不公開的,例如 Mac SQLiteManager 生成的加密的 .db 文件沒法在程序里面解密打開。程序里面生成的加密的 .db 文件也沒法用 Mac 上的 SQLiteManager 打開。
免費版本的項目代碼不提供以下的功能:
- 數據庫創建的時候,沒有使用 sqlite3_key 設置密碼,之后不能添加密碼管理;
- 對創建時已經設置了密碼管理的數據庫,不能取消其密碼管理,只能重新設置新的密碼;
