SQLCipher官方提示无法直接使用sqlite3_rekey加密,需要额外的采用sqlcipher_export方法来对已有未加密数据库加密,官方提供的步骤是如下:

$ ./sqlcipher plaintext.db
sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';
sqlite> SELECT sqlcipher_export('encrypted');
sqlite> DETACH DATABASE encrypted;

   实际使用过程中,发现对比较大(例如一两百兆)的数据库加密耗时非常久的情况,查看文件大小可以看到每秒几K~十几KB的增长,这对于升级客户端数据库是不大现实的。后面艰难的查找资料(基本没有这个问题讨论,偶然间看到别人使用微信数据库升级的代码发现了原因),实验对比问题是出在sqlcipher_export函数执行前后没有添加数据库事务的原因。调整加密步骤如下即可:

1)打开已有的数据库

2)执行ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';

3)执行begin开启数据库事务

4)执行SELECT sqlcipher_export('encrypted');执行该语句前后需要注意开启事务必须开启

5)执行commit关闭数据库事务

6)DETACH DATABASE encrypted;

本质是实时导出迁移数据,原来刚开始使用时性能非常低(几KB~十几KB每秒),添加了步骤3和5后,137MB的数据库加密耗时在12秒。对于已有数据库存在大量数据库表(如超过1万)的情况,进行该加密耗时也是非常久,实测来看1小时都还没执行完成,暂停查看堆栈主要操作耗时在导出表结构创建到新数据库的过程。

另外,这里加密出来的新数据库实际是一个新的数据库文件,原有数据库文件仍然保留,而且加密后的数据库通常会比原来的数据库大一些,通常增大1-20%不等,单表大量记录(30万)的极端情况也有出现增长65%的情况,所以比较保险的是迁移前预留2倍数据库大小的空间。

PS:调研过程,发现wxSQLite的rekey操作基本无法对已有未加密数据库加密,几百KB的db可以操作成功,2MB以上的数据库进行rekey操作就会直接崩溃,调试执行分析是执行vacuum过程中出现建表失败返回值为数据库损坏11(实际没有损坏)的错误,崩溃实际是由于字符串指针未初始化导致(不明白为什么有这么低级的错误),字符串指针初始化为NULL后解决崩溃问题可以正常执行rekey结束,但是没有加密成功