SQLCipher

SQLite是一個輕量的、跨平臺的阵子、開源的數(shù)據(jù)庫引擎拌禾,它的在讀寫效率、消耗總量闽晦、延遲時間和整體簡單性上具有的優(yōu)越性,使其成為移動平臺數(shù)據(jù)庫的最佳解決方案(如iOS提岔、Android)仙蛉。然而免費版的SQLite有一個致命缺點:不支持加密。這就導(dǎo)致存儲在SQLite中的數(shù)據(jù)可以被任何人用任何文本編輯器查看到碱蒙。

如果我們想要使得自己的數(shù)據(jù)庫加密荠瘪,解決方案就是使用另一款開源的加密數(shù)據(jù)庫SQLCipher夯巷,SQLCipher使用256-bit AES加密,由于其基于免費版的SQLite哀墓,主要的加密接口和SQLite是相同的趁餐,當然也增加了一些自己的接口,如在新建和打開數(shù)據(jù)庫時篮绰,給數(shù)據(jù)庫設(shè)置秘鑰之類的操作后雷。

FMDB是一個開源的類庫,它對sqlite數(shù)據(jù)庫操作進行了很不錯的封裝吠各,而且也增加了對sqlcipher的支持臀突,也就是說,我們不直接用sqlcihper也能完成加解密操作贾漏,而且FMDB在操作sqlite方面方便得多『蜓В現(xiàn)在的APP開發(fā)如果涉及到數(shù)據(jù)庫操作,F(xiàn)MDB基本上是首選纵散。

下面內(nèi)容主要是針對一些在初期忽略了數(shù)據(jù)庫私密性梳码,而到了中期需要讓數(shù)據(jù)庫進行升級加密的App的簡單方案介紹和代碼實現(xiàn)。如果讀者沒有使用FMDB伍掀,直接使用了sqlite边翁,那本文也有一定參考性硕盹,只是關(guān)于SQLCipher的配置符匾,并沒有介紹,因為雖然FDMB的加密也是使用SQLCipher瘩例,但是不需要進行配置的啊胶。

具有加密功能的FMDB版本

加密的FMDB其實是一個分支,也就是說垛贤,如果你需要替換FMDB焰坪。Github上關(guān)于該分支的安裝只提供了cocospod的安裝方式。

Github上FMDB的地址:https://github.com/ccgus/fmdb
sqlcipher:https://github.com/sqlcipher/sqlcipher

舉個例子聘惦,如果你以前是如下寫的

pod 'FMDB'

如果想換成有加密功能的某饰,就改成

pod 'FMDB/SQLCipher'

升級數(shù)據(jù)庫代碼實現(xiàn)

得到對的版本后,我們需要在代碼里做一些處理善绎,讓舊數(shù)據(jù)庫升級黔漂。這里升級包括兩點:

  • 1 是我們前面所說的,將數(shù)據(jù)庫加密禀酱。
  • 2 把舊數(shù)據(jù)庫的表和數(shù)據(jù)遷移到新數(shù)據(jù)庫中炬守。

sqlcipher的使用和sqlite沒有多大的區(qū)別,有一點值得注意便是每次當[dp open]成功后剂跟,需要給數(shù)據(jù)庫配上口令减途,即[db setKey:DB_SECRETKEY]酣藻,DB_SECRETKEY是我們的一個自定義宏。這之后鳍置,我們才能讀出數(shù)據(jù)庫的內(nèi)容辽剧。

FMDatabase *_db = [FMDatabase databaseWithPath:[cachePath stringByAppendingString:dbFileName]];
if (![_db open]) {
    _db = nil;
    return;
}else{
    [_db setKey:DB_SECRETKEY];
}

然后我們需要判斷數(shù)據(jù)庫是否需要升級,當使用sqlchipher打開用sqlite生成的源數(shù)據(jù)庫時税产,[dp goodConnection]是為NO怕轿,即使上面[db open]操作是YES。

if(![_db goodConnection]){ //無效連接
    [self upgradeDatabase:dbpath];
}

接下來砖第,便是數(shù)據(jù)遷移,path是我們的源數(shù)據(jù)庫路徑环凿,假設(shè)我們的源數(shù)據(jù)庫名字為DBName梧兼,changeDatabasePath即將我們的數(shù)據(jù)庫A改名為DBName.tmp,接下來便是智听,新建數(shù)據(jù)庫B羽杰,因為A在前面已經(jīng)進行了改名為DBName.tmp,并且已經(jīng)B將來就是我們要取代A的數(shù)據(jù)庫到推,所以此處的B名字便是DBName考赛。最后將DBName.tmp的數(shù)據(jù)復(fù)制到DBName中,再刪除DBName.tmp莉测,致此颜骤,我們的數(shù)據(jù)庫便升級完成了。

- (void)upgradeDatabase:(NSString *)path{
    NSString *tmppath = [self changeDatabasePath:path];
    if(tmppath){
        const char* sqlQ = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '%@';",path,DB_SECRETKEY] UTF8String];
         
        sqlite3 *unencrypted_DB;
        if (sqlite3_open([tmppath UTF8String], &unencrypted_DB) == SQLITE_OK) {
             
            // Attach empty encrypted database to unencrypted database
            sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, NULL);
             
            // export database
            sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, NULL);
             
            // Detach encrypted database
            sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, NULL);
             
            sqlite3_close(unencrypted_DB);
             
            //delete tmp database
            [self removeDatabasePath:tmppath];
        }
        else {
            sqlite3_close(unencrypted_DB);
            NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));
        }
    }
}
- (NSString *)changeDatabasePath:(NSString *)path{
    NSError * err = NULL;
    NSFileManager * fm = [[NSFileManager alloc] init];
    NSString *tmppath = [NSString stringWithFormat:@"%@.tmp",path];
    BOOL result = [fm moveItemAtPath:path toPath:tmppath error:&err];
    if(!result){
        NSLog(@"Error: %@", err);
        return nil;
    }else{
        return tmppath;
    }
}

經(jīng)過上面步驟捣卤,我們知道雖然sqlchipher是基于sqlite的忍抽,但到底還是不一樣的,我們沒辦法直接將sqlite的數(shù)據(jù)庫升級為sqlchipher董朝,只能用sqlchipher新建一個數(shù)據(jù)庫鸠项,再重新寫入數(shù)據(jù)。

以上代碼僅僅是范例子姜,各個App的數(shù)據(jù)存儲模型不盡相同祟绊,這里我也沒辦法給出模板。雖然代碼不一定是適用的哥捕,但是在數(shù)據(jù)庫升級時牧抽,代碼執(zhí)行的先后順序是肯定的:打開數(shù)據(jù)庫open -> 設(shè)置秘鑰 setkey -> 查看連接 goodConnection -> 新建數(shù)據(jù)庫并遷移數(shù)據(jù) upgrade。

參考:
關(guān)于SQLite遥赚,SQLCipher和FMDB
app數(shù)據(jù)庫版本遷移(sqlite表結(jié)構(gòu)變更)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阎姥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鸽捻,更是在濱河造成了極大的恐慌呼巴,老刑警劉巖泽腮,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異衣赶,居然都是意外死亡诊赊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門府瞄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碧磅,“玉大人,你說我怎么就攤上這事遵馆【ń迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵货邓,是天一觀的道長秆撮。 經(jīng)常有香客問我,道長换况,這世上最難降的妖魔是什么职辨? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮戈二,結(jié)果婚禮上舒裤,老公的妹妹穿的比我還像新娘。我一直安慰自己觉吭,他們只是感情好腾供,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鲜滩,像睡著了一般台腥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绒北,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天黎侈,我揣著相機與錄音,去河邊找鬼闷游。 笑死峻汉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的脐往。 我是一名探鬼主播休吠,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼业簿!你這毒婦竟也來了瘤礁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤梅尤,失蹤者是張志新(化名)和其女友劉穎柜思,沒想到半個月后岩调,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡赡盘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年号枕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陨享。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡葱淳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抛姑,到底是詐尸還是另有隱情赞厕,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布定硝,位于F島的核電站皿桑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏喷斋。R本人自食惡果不足惜唁毒,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一蒜茴、第九天 我趴在偏房一處隱蔽的房頂上張望星爪。 院中可真熱鬧,春花似錦粉私、人聲如沸顽腾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抄肖。三九已至,卻和暖如春窖杀,著一層夾襖步出監(jiān)牢的瞬間漓摩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工入客, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留管毙,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓桌硫,卻偏偏與公主長得像夭咬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铆隘,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353