iOS SQLite加密之SQLCipher

  至于SQLCipher,我就不多说了,百度一下,你就知道。公司的iOS APP要给SQLite,今天就上网查了些资料,做了下整理,主要参考SQLCipher的官方集成文档,有不妥之处大家提出来我再改进,感谢拍砖。官方集成文档:https://www.zetetic.net/sqlcipher/ios-tutorial/。

Prerequisites

安装Xcode开发环境,不用多说了吧。

OpenSSL

本教程用苹果提供的FrameWork:CommonCrypto,可在工程,Target,Build Phases ,Link Binary 中添加,搜索 CommonCrypto即可添加。

SQLCipher

打开终端,切到你要集成sqlcipher的工程根目录下,执行下命令。从GitHub下载 SQLCipher到本地:

$ cd ~/Documents/code/SQLCipherApp
$ git clone https://github.com/sqlcipher/sqlcipher.git

Xcode Project Configuration

The SQLCipher source provides a sqlcipher.xcodeproj project file that we‘ll add to your project to build a static library that you‘ll link from your main application target.

Add Project Reference

选择你的项目,右键,选择 "Add Files to [你的工程]";在弹出的选择窗口找到你刚才从git下载的sqlcipher路径,打开sqlcipher文件夹,选择sqlcipher.xcodeproj;

技术分享

注意:不要勾选 Copy items if needed 复选框。

技术分享

Configure Target Dependencies and Link Binary With Libraries

点击工程,选择TARGETS中你的工程,点击 Build Phases Tab栏。

1. 展开 Target Dependencies ,添加 + sqlcipher 静态库

技术分享

技术分享

2.展开 Link Binary With Libraries,添加+libsqlcipher.a

技术分享

注意:如果你的工程库中已经添加了libsqlite3.dylib 或者其他的SQLite库,请Remove掉,否则可能会提示出现重复sqlite库。

重复这些步骤在你工程的其他Target中。

Configure Build Settings

回到你的工程编辑面板,选择工程,TARGETS》你的工程Target,Build Settings Tab栏,

1.选择Header Search Paths项,双击键入新值:./sqlcipher/src。如图:

技术分享

注意填写的路径。../表示上级文件夹

2.选择 Other C Flags ,双击 添加-DSQLITE_HAS_CODEC。注意Release 和 Debug的配置值一样,如下图:

技术分享

注意(自己翻译吧):Hot Tip: The handling of Architectures and the meaning of the $(STANDARD_ARCHS) build variable changes depending on the Xcode release, but currently on iOS in Xcode 6.1.1 the armv7s architecture is not included in your build. SQLCipher supports this architecture! If you need to squeeze that much more speed out of SQLCipher on those devices, add the armv7s architecture string to your Architectures option in Build Settings (sample image here).

Integration Code

到现在为止,sqlcipher已经包含到我们的工程中了,现在我们可以使用sqlcipher来创建或打开一个加密sqlite书库了。接下来打开程序的代理类,引入sqlite3.h头文件,找到-applicationDidFinishLaunching:withOptions:代理方法,开始设置代码如下:

//
//  AppDelegate.m
//  SecureLoginDelegate
//
//  Created by Billy Gray on 10/19/14.
//  Copyright (c) 2014 Zetetic. All rights reserved.
//
 
#import "AppDelegate.h"
#import <sqlite3.h>
 
@interface AppDelegate ()
@property (nonatomic) BOOL isLoginViewControllerDisplayed;
@property (readonly) NSURL *databaseURL;
@property (readonly) BOOL databaseExists;
@end
 
@implementation AppDelegate
@dynamic databaseURL;
@dynamic databaseExists;
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Set up the window with loginViewController as the rootViewController for now
    // to avoid showing app view on launch on iOS 8
    [[self window] setRootViewController:self.loginViewController];
    [[self window] makeKeyAndVisible];
    self.isLoginViewControllerDisplayed = YES;
    
    // Set up a SQLCipher database connection:
    sqlite3 *db;
    if (sqlite3_open([[self.databaseURL path] UTF8String], &db) == SQLITE_OK) {
        const char* key = [@"StrongPassword" UTF8String];
        sqlite3_key(db, key, (int)strlen(key));
        if (sqlite3_exec(db, (const char*) "SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
            NSLog(@"Password is correct, or a new database has been initialized");
        } else {
            NSLog(@"Incorrect password!");
        }
        sqlite3_close(db);
    }
    return YES;
}
 
- (NSURL *)databaseURL {
    NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSURL *directoryURL = [URLs firstObject];
    NSURL *databaseURL = [directoryURL URLByAppendingPathComponent:@"secure.db"];
    return  databaseURL;
}
 
- (BOOL)databaseExists {
    BOOL exists = NO;
    NSError *error = nil;
    exists = [[self databaseURL] checkResourceIsReachableAndReturnError:&error];
    if (exists == NO && error != nil) {
        NSLog(@"Error checking availability of database file: %@", error);
    }
    return exists;
}
@end

SecureLoginDelegate-AppDelegate.m hosted with ? by GitHub

OK,编译Run你的App,配置不错的话就能运行成功了,在控制台你若能看到打印如下的日志内容:

技术分享

虽然我们建议你用sqlite3_bind_* API 来避免一些陷阱,你也可以执行PRAGMA key = ‘some key‘; as SQL。

下面还有一段英文自己看吧:

The call to sqlite3_key or "PRAGMA key" should occur as the first operation after opening the database. In most cases SQLCipher uses PBKDF2, a salted and iterated key derivation function, to obtain the encryption key. Alternately, an application can tell SQLCipher to use a specific binary key in blob notation (note that SQLCipher requires exactly 256 bits of key material), for example:

PRAGMA key = "x‘2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99‘";

Once the key is set SQLCipher will automatically encrypt all data in the database! Note that if you don‘t set a key then SQLCipher will operate identically to a standard SQLite database.

After the application is wired up to use SQLCipher, take a peek at the resulting data files to make sure everything is in order. An ordinary SQLite database will look something like the following under hexdump. Note that the file type, schema, and data are clearly readable.

% hexdump -C plaintext.db 
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  04 00 01 01 00 40 20 20  00 00 00 04 00 00 00 00  |.....@  ........|
...
000003b0  00 00 00 00 24 02 06 17  11 11 01 35 74 61 62 6c  |....$......5tabl|
000003c0  65 74 32 74 32 03 43 52  45 41 54 45 20 54 41 42  |et2t2.CREATE TAB|
000003d0  4c 45 20 74 32 28 61 2c  62 29 24 01 06 17 11 11  |LE t2(a,b)$.....|
000003e0  01 35 74 61 62 6c 65 74  31 74 31 02 43 52 45 41  |.5tablet1t1.CREA|
000003f0  54 45 20 54 41 42 4c 45  20 74 31 28 61 2c 62 29  |TE TABLE t1(a,b)|
...
000007d0  00 00 00 14 02 03 01 2d  02 74 77 6f 20 66 6f 72  |.......-.two for|
000007e0  20 74 68 65 20 73 68 6f  77 15 01 03 01 2f 01 6f  | the show..../.o|
000007f0  6e 65 20 66 6f 72 20 74  68 65 20 6d 6f 6e 65 79  |ne for the money|

Fire up the SQLCipher application in simulator and look for the application database files under/Users/billy/Library/Developer/CoreSimulator/Devices/<SOME ID NUMBER>/data/Containers/Data/Application/<SOME ID NUMBER>/Documents(step through the code in the debugger and enter po [self.databaseURL path] for the exact path). Try running hexdump on the application database. With SQLCipher the output should looks completely random, with no discerning characteristics at all.

% hexdump -C sqlcipher.db 
00000000  1b 31 3c e3 aa 71 ae 39  6d 06 f6 21 63 85 a6 ae  |.1<..q.9m..!c...|
00000010  ca 70 91 3e f5 a5 03 e5  b3 32 67 2e 82 18 97 5a  |.p.>.....2g....Z|
00000020  34 d8 65 95 eb 17 10 47  a7 5e 23 20 21 21 d4 d1  |4.e....G.^# !!..|
...
000007d0  af e8 21 ea 0d 4f 44 fe  15 b7 c2 94 7b ee ca 0b  |..!..OD.....{...|
000007e0  29 8b 72 93 1d 21 e9 91  d4 3c 99 fc aa 64 d2 55  |).r..!...<...d.U|
000007f0  d5 e9 3f 91 18 a9 c5 4b  25 cb 84 86 82 0a 08 7f  |..?....K%.......|
00000800  

See Also

All applications that make use of cryptography, including those that use SQLCipher or iOS internal libraries like CommonCrypto and Keychain, must provide documentation to Apple that demonstrates review by the Department of Commerce (DOC) Bureau of Industry and Security (BIS) and classification of the application a mass market encryption item.

Information on the PBKDF2 key derivation function is available at http://en.wikipedia.org/wiki/PBKDF2

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。