美文网首页iOS开发技术数据库和缓存iOS Developer
iOS数据库加密完全手册——FMDB+SQLCipher

iOS数据库加密完全手册——FMDB+SQLCipher

作者: 7544eb39c4ac | 来源:发表于2016-08-25 14:49 被阅读1618次

    这篇文章介绍如何对数据库文件进行加密,基于FMDB+SQLCipher,标明踩过的坑,留给同样掉进坑里的小伙伴参考。

    1、下载支持SQLCipher的FMDB分支

    FMDB官方库地址:https://github.com/ccgus/fmdb

    SQLCipher官网:https://www.zetetic.net/sqlcipher/

    作者做了一个支持SQLCipher的仓库,为我们节约了大量编译和工程配置时间,感谢!

    修改Podfile:

    pod 'FMDB'

    替换为

    pod 'FMDB/SQLCipher'

    更新一下Pod即可得到支持SQLCipher的FMDB。

    2、加密方法的使用

    - (BOOL)setKey:(NSString*)key;

    方法很简单,关键是使用的位置:

    2.1、直接使用FMDatabase

    每次调用

    [db open]

    的时候调用一次

    2.2、使用FMDatabaseQueue

    调用

    + (instancetype)databaseQueueWithPath:(NSString*)aPath

    的时候调用一次 ,例如:

    self.dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath]

    [self.dbQueue inDatabase:^(FMDatabase*db) {

        [db setKey:kDBEncryptedKey]

    }];

    这里有个坑:不是每次调用

    - (void)inDatabase:(void(^)(FMDatabase*db))block

    的时候都要在block里调用

    - (BOOL)setKey:(NSString*)key

    只需要在初始化FMDatabaseQueue完的时候调用一次即可,而且要保证最先调用,不然任何数据库操作都是非法的!


    3、非加密数据库<->加密数据库

    如何将现有非加密数据库在保存数据的情况下转换为加密数据库?

    又如何将加密数据库保留数据还原为非加密数据库?

    这里需要用到sqlcipher_export()方法。

    3.1、创建全新的加密数据库

    如果待创建的数据库是全新的,按照上面说的方法直接调用

    - (BOOL)setKey:(NSString*)key

    即可得到加密数据库。

    3.2、将现有非加密数据库转换为加密数据库

    如果你的数据库开始是没有加密的,按照上面介绍的方法调用

    - (BOOL)setKey:(NSString*)key

    并不会将现有数据库转换为加密数据库,只会得到一大堆报错。

    下面是一段从SQLCipher拷贝来的例子:

    Example 1: Encrypt a Plaintext Database

    $ ./sqlcipher plaintext.db

    sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';

    sqlite> SELECT sqlcipher_export('encrypted');

    sqlite> DETACH DATABASE encrypted;

    我们只需要执行三个SQL语句即可将非加密数据库拷贝到加密数据库,是不是很简单?但是这里又有个坑:

    1、sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';

    这句话中的'encrypted.db',代表待创建的加密数据库的全路径,并不只是数据库的名称;

    2、sqlite> SELECT sqlcipher_export('encrypted');

    这句话中的'encrypted',代表一个关键字!!!并不是什么待创建的数据库的全路径也不是什么数据库名称!或许真的是我太单纯了...

    3、sqlite> DETACH DATABASE encrypted;

    没错,这里的encrypted也是一个关键字

    使用代码封装一下:

    #define kDBEncryptedKey@"abcdefg"

    NSString *encryptedDBPath = @"Your/DB/Path/encryptedDB.db"

    NSString *plainttextDBPath = @"Your/DB/Path/plainttextDB.db"

    const char *sql = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '%@';", encryptedDBPath, kDBEncryptedKey] UTF8String];

    const char *exportSql = [[NSString stringWithFormat:@"SELECT sqlcipher_export('encrypted');"] UTF8String];

    const char *detachSql = [[NSString stringWithFormat:@"DETACH DATABASE encrypted;"] UTF8String];

    sqlite3 *unencrypted_DB = NULL;

    if(sqlite3_open([plainttextDBPath UTF8String], &unencrypted_DB) ==SQLITE_OK)

    {

        int rc;

        char *errmsg = NULL;

        rc =sqlite3_exec(unencrypted_DB, sql,NULL,NULL, &errmsg);

        rc =sqlite3_exec(unencrypted_DB, exportSql,NULL,NULL, &errmsg);

        rc =sqlite3_exec(unencrypted_DB, detachSql,NULL,NULL, &errmsg);

        sqlite3_close(unencrypted_DB);

    }

    else

    {

        sqlite3_close(unencrypted_DB);

        NSAssert1(NO,@"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));

    }

    通过上面的代码可以从非加密数据库拷贝一份保留数据的加密数据库,加密数据库生成后记得删除旧数据库。

    3.3、将加密数据库转换为非加密数据库

    下面又是一段从SQLCipher拷贝来的例子:

    Example 2: Decrypt a SQLCipher database to a Plaintext Database

    $ ./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;

    记得上面说的坑:

    'plaintext.db'是待创建的未加密数据的全路径;

    'plaintext'是关键字,不是路径也不是去掉后缀的数据库名称;

    plaintext是关键字;

    使用代码封装一下:

    #define kDBEncryptedKey@"abcdefg"

    NSString *encryptedDBPath = @"Your/DB/Path/encryptedDB.db"

    NSString *plaintextDBPath = @"Your/DB/Path/plainttextDB.db"

    const char *sql = [[NSStringstringWithFormat:@"PRAGMA key = '%@';", kDBEncryptedKey] UTF8String];

    const char *attachSql = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS plaintext KEY '';", plaintextDBPath] UTF8String];

    const char *exportSql = [[NSStringstringWithFormat:@"SELECT sqlcipher_export('plaintext');"] UTF8String];

    const char *detachSql = [[NSStringstringWithFormat:@"DETACH DATABASE plaintext;"] UTF8String];

    sqlite3*encrypted_DB =NULL;

    if(sqlite3_open([encryptedDBPathUTF8String], &encrypted_DB) ==SQLITE_OK)

    {

        int rc;

        char *errmsg = NULL;

        rc = sqlite3_exec(encrypted_DB, sql,NULL,NULL, &errmsg);

        rc =sqlite3_exec(encrypted_DB, attachSql,NULL,NULL, &errmsg);

        rc =sqlite3_exec(encrypted_DB, exportSql,NULL,NULL, &errmsg);

        rc =sqlite3_exec(encrypted_DB, detachSql,NULL,NULL, &errmsg); 

        sqlite3_close(encrypted_DB);

    }

    else

    {

        sqlite3_close(encrypted_DB);

        NSAssert1(NO,@"Failed to open database with message '%s'.", sqlite3_errmsg(encrypted_DB));

    }

    使用上面的代码就可以将加密后的数据库还原成未加密数据库,同样记得删除旧数据库。

    坑:转换后的数据库除了保留了数据外,并没有保留以前的数据库附加信息,例如userVersion,记得将老数据库的userVersion赋给新数据库,不然数据库升级可能要有麻烦了!

    关键代码都在这里,大家根据个人需要自由组合,例子我就不写了,因为懒。

    相关文章

      网友评论

      • 淡语52188:💐💐💐
      • RamboMing:你好,请问$ ./sqlcipher encrypted.db 这句话是什么含义,我在网上找到sqlcipher-shell64.exe 可以执行程序,执行解密的那4句命令,并没有成功解密
      • 863c73f31933:打扰作者了,我这边没有cocoapods导入fmdb和sqlcipher,都是自己手动导入,fmdb导入成功,我看官方步骤上面也没说要加openssl这个库,在导入sqlcipher的时候报说没有添加openssl库,这个库要怎么加进去?作者知道不?
        863c73f31933:@共产主义接班人被注册了 我这边参考了一个资料,已经实现了手动导入,不麻烦的,链接地址,http://www.jianshu.com/p/bd7845062cc8?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends。我实现这个功能之后,遇到了无分查看加密后的数据库,作者知道怎么查看加密后的数据库?
        7544eb39c4ac:@SunlightInMyLif https://www.zetetic.net/sqlcipher/ios-tutorial/
        7544eb39c4ac:@SunlightInMyLif 我没有手动倒入过,貌似很麻烦。不过建议参考下这里https://github.com/datatheorem/SQLCipher-iOS

      本文标题:iOS数据库加密完全手册——FMDB+SQLCipher

      本文链接:https://www.haomeiwen.com/subject/dytasttx.html