wcdb使用笔记

本地数据加密

由于项目涉及到一些用户隐私数据的存储,所以需要对保存在客户端本地的数据进行加密,以防止用户隐私数据在设备被root的情况下出现泄漏。目前android的本地数据存储基本分为file,sharepreference和database,所以对数据的加密操作分为了两种:文件加密和文件内的数据加密。文件加密就是在打开该文件的时候需要获得正确的加密秘钥才能从该文件中读取数据或者写入数据到该文件中,这种方式相对简单。文件内数据加密就是打开文件时不需要解密,但是从文件中读取出来的数据是加密过的密文,需要对其进行解密才能识别和使用,同样在写入数据到文件的时候,也需要先将数据进行加密然后再写入到文件,这种方式相比第一种复杂一些,但是安全性更高,对数据保护的粒度更细。file和sharepreference使用上述两种方式实现的成本差异并不明显,database使用文件内数据比如列字段加密相比文件加密要更加复杂,所以database通常使用文件加密的方式来实现,比如有名的sqlcipher就是采用的这种方式,今天我们将要提到的wcdb也是采用的这种方式来保护数据的。

WCDB

wcdb是微信团队贡献的一个开源项目,是一个高效易用的数据库框架,并且支持多个平台。关于wcdb的更多介绍以及如何集成和使用WCDB请参考Tencent/wcdb,这里不再赘述。下面主要介绍一下我在将WCDB集成到原有项目(Android客户端)的过程中遇到的一些问题以及解决方案。因为我也是在使用WCDB的过程中不断查找资料,发现了一些别人没有遇到或者别人遇到了自己也遇到了但是没有清楚答案的问题,所以才想记录下来,以作备忘。

混淆

通常我们在引入一些知名的第三方库的时候,都需要在proguard中加入一些规则来屏蔽对该库的混淆,因为混淆可能会导致该库的部分功能异常,比如glide的项目介绍上就有如下说明:

image

但是我们在wcdb的项目上没有看到关于混淆这一块的介绍,刚开始我以为不需要,后来打了一个release包后发现wcdb运行报错,才知道这里还是有必要加一下的。内容如下:

-keep class com.tencent.wcdb.** {*;}

关于在proguard中如何加入第三方库的放混淆配置,可以使用以下方法。在android studio的project视图下的External Libraries中找到对应的库名字,比如wcdb,然后就可以看到这个库的完整包路径了。如下图:


image

加密已有数据

由于我的项目以前是使用android原生的sqlite储存数据,现在要迁移到wcdb上,就必须考虑到版本兼容的问题,当老版本升级到新版本后确保老版本上保存在本地的数据能无缝迁移到新版本上面来。那么对于已有数据的加密,wcdb的项目里面也提供了一个例子,我将核心代码贴出来:

File oldDbFile = mContext.getDatabasePath(OLD_DATABASE_NAME);
if (oldDbFile.exists()) {
    // SQLiteOpenHelper begins a transaction before calling onCreate().
    // We have to end the transaction before we can attach a new database.
    db.endTransaction();

    // Attach old database to the newly created, encrypted database.
    String sql = String.format("ATTACH DATABASE %s AS old KEY '';",
            DatabaseUtils.sqlEscapeString(oldDbFile.getPath()));
    db.execSQL(sql);

    // Export old database.
    db.beginTransaction();
    DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('main', 'old');", null);
    db.setTransactionSuccessful();
    db.endTransaction();

    // Get old database version for later upgrading.
    int oldVersion = (int) DatabaseUtils.longForQuery(db, "PRAGMA old.user_version;", null);

    // Detach old database and enter a new transaction.
    db.execSQL("DETACH DATABASE old;");

    // Old database can be deleted now.
    oldDbFile.delete();
}

这里很多第一次使用wcdb的童鞋,如果没有细看这个例子中的代码会比较容易犯一个错误,就是将

DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('main', 'old');", null);

不小心写成了

db.execSQL("SELECT sqlcipher_export('encrypted')");

然后发现怎么运行都会报异常。针对这个问题在项目里面还有一个issue:集成WCDB后希望能实现解密数据库 #36。我也遇到了,后面找了好久才发现是这里的问题,原因是wcdb的execSQL不支持select,但是原生的sqlite以及sqlcipher都是支持,所以在第一次用的时候就会觉得很奇怪,这条语句语法看上去完全没问题,运行就是行不通。

数据解密

有时为了方便定位问题,需要查看database的数据,如果是debug包可以直接联调查看,但如果是release包那么就必须手动导出db文件然后进行解密查看了。这里有两种方式解密:

  1. db文件拷贝到电脑上面,然后安装sqlcipher工具进行解密
  2. 在手机上使用wcdb sdk来解密

电脑端使用sqlcipher解密

首先在电脑端安装sqlcipher工具(链接:https://pan.baidu.com/s/1_yCOoqZJTersQ6KmkZb13w
提取码:jorr ),这里以windows为例,下载该工具后进入sqlcipher-3.0.1\bin\目录下,打开命令行工具,输入以下命令,如下图:

image
image

其中123456为加密秘钥,encrypt.db为加密的数据库文件,这里有几个地方需要注意:

  1. wcdb默认加密后的db文件的pagesize为4096,所以这里如果不设置cipher_page_size或者设置的值与你在使用wcdb加密时设置的pagesize一致的话,就会报错,这一点我网上找了很久都没发现有人提到,有的文章上设置的是1024,但是我试过就是不行,后来找了一个未加密的db文件看了下pagesize的值才发现不对。
  2. 在进行任何操作之前需要先使用pragma key=...来解密数据库,否则可能会报错“Error: file is encrypted or is not a database”,这里网上也有很多人跟我一样遇到。
  3. wcdb使用了sqlcipher来加密的,在加解密的时候必须使用一致的版本,比如我们使用sqlcipher3.x加密的,那么在解密的时候也必须使用3.x版本,否则就会解密失败。

有时在命令行里面解密和查看数据不太方便,我们可以将加密db中的数据导出到一个未加密的db中,首先我们在命令行工具中使用sqlcipher打开encrypt.db文件,然后输入如下命令:

pragma key='123456';
pragma cipher_page_size=4096;
attach database 'plaintext.db' as plaintext key '';
select sqlcipher_export('plaintext');
detach database plaintext;

其中123456为加密秘钥,palintext.db为解密后的db文件。执行完上述命令后,我们就会在当前目录下看到一个解密后的plaintext.db文件了,然后使用其他的数据库工具如sqlite expert等就可以正常打开查看里面的数据了。

wcdb sdk解密

使用wcdb sdk解密数据与之前提到的加密数据的过程是相识的,这里结合代码来详细说明。

SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(getDataBase("encrypt.db"), "123456".getBytes(), null, null);
//将要生成的未加密db文件,这里可以根据自己的需要放在sd目录中方便导出查看
File plainDbFile = mContext.getDatabasePath("plaintext.db");
// Attach database 
String sql = String.format("ATTACH DATABASE %s AS plaintext KEY ';",
        DatabaseUtils.sqlEscapeString(plainDbFile.getPath()));
db.execSQL(sql);

//导出加密数据库到未加密数据库中,sqlcipher_export这个方法有两个参数,第一个参数plaintext代表新生成的未加密数据库,第二个参数main代表已加密的数据库,也可以不使用第二个参数,那么默认将使用db关联的数据库导出到plaintext中。详细使用可以查看wcdb开源项目的android demo
db.beginTransaction();
DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('plaintext', 'main');", null);
db.setTransactionSuccessful();
db.endTransaction();

// Detach plaintext database
db.execSQL("DETACH DATABASE plaintext;");

这样我们就将一个加密数据库导出到了plaintext.db中。

总结

本文算是自己在项目中使用WCDB过程中一些使用心得和问题总结,WCDB在使用这一块其实还有更多高级的用法,这里并没有提到,后面有时间了再做详述。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359

推荐阅读更多精彩内容

  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,189评论 22 257
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,336评论 0 9
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,947评论 2 89
  • 今天是复盘李笑来老师《通往财富自由之路》的第二周,这周主题是人生最宝贵的财富是什么? 如果先前有人问...
    张占杰阅读 317评论 0 0
  • 17.痴情人,误入天机山 秋风,恼人的秋风,古人云:"自古逢秋悲寂寥。"原本寂寥的秋,再加上这一缕秋风,更...
    柏谷阅读 340评论 0 0