在 Node.js 中使用 SQLCipher

经过试验, 使用 Electron 打包之后我们实现的加密代码并没有打入. 经过排查, 发现是由于在 Electron 重新编译代码的过程中, 从服务器端下载了已经官方提供的编译好的工具, 并没有使用我们自己的工具. 因此这里需要自己对 Node-SQLite 代码做出修改. 此外, 由于尽量少的采用 dll 的原则, 因此对于 SQLCipher 我们采用了静态库链接的方式. 修改后的 Node-Sqlite3

命令行安装

网络上查到的命令安装方式:

  • 在 Windows 上执行以下命令

    npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=yourlib --verbose
    
  • 在 Mac 上执行以下命令

    export LDFLAGS="-L/yourlib"
    export CPPFLAGS="-I/yourlib/include -I/yourlib/include/sqlcipher -I/yourlib/include/openssl" 
    export CXXFLAGS="-I/yourlib/include -I/yourlib/include/sqlcipher -I/yourlib/include/openssl" 
    npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --openssl_libname=crypto --sqlite=yourlib --verbose
    

    这个时候, 在本机上你就已经根据你的 sqlcipher 安装好了一份加密的 sqlite3. 但是这里有个问题, 所有的库全部都是动态库!. 由于我们不想使用 dll ,因此需要对 Node-Sqlite3 进行修改. 这个修改会在会面做出详解.

    修改后执行以下命令, sqlcipher 就安装好了.

    npm install git+https://github.com/chinaofmelon/node-sqlite3.git --build-from-source --verbose
    

注意:

  1. 当 lib 中存在有动态库的时候, 默认会安装动态库. 但是我们需要连接静态库. 这时候删除动态库就可以了. 但是 sqlcipher 的静态库依赖于 openssl 的静态库, 因此需要将 openssl 的库也放到 yourlib 中, 并指定名称.

  2. 由于默认的 node-sqlite3 只有一个参数, 因此我对其做了修改, 增加了一个 openssl_lib 的参数适应静态编译过程.

使用 package.json 安装

但是大部分的时候, 我们都是很多开发人员共同开发. 那么一般情况下, 就是在工程中创建一个 package.json 文件, 然后开发人员在各自的环境中进行安装各个模块. 这时候上面的命令就用不了了. 我的做法是, 在 git 仓库上自建一个 npm package. 在这个 package 内部, 根据平台不同执行不同的编译命令, 以提供在不同的平台上使用.

如下:

package.json

{
    ...
    "dependencies": {
        ...
        "sqlite3": "git+https://github.com/chinaofmelon/node-sqlite3.git",
        ...
    }
    ...
}

postinstall.js

require('shelljs/global');
var isArray = require('util').isArray;

var args;
try {
    args = JSON.parse(process.env.npm_config_argv).original
} finally {
    if (!isArray(args)) {
        args = [];
    }
}
var targetArgs = args.filter(function (arg) {
    return /^--(runtime|target)/.test(arg);
})
var targetStr = targetArgs.reduce(function (m, arg) {
    return m + ' ' + arg;
}, '');

if (process.platform == 'win32') {
    // WINDOWS
    exec("cd node_modules\\sqlite3 && npm install --build-from-source");
} else if (process.platform === 'darwin') {
    // MAC
    exec("cd node_modules/sqlite3 && npm install --build-from-source");
} else {
    // linux
    console.warn("[AKSQLiteCipher] Do not support linux OS yet");
    exit(1);
}

至此, 执行 npm install, SQLCipher 就已经安装好了.

使用 js 代码进行测试:

// node test-sqlcipher-fts.js
'use strict';

var sqlite3 = require('你的名字');
var db = new sqlite3.Database('./test.sqlcipher');

db.serialize(function() {
  var stmt
    , messages
    ;

  db.run("PRAGMA KEY = 'secret'");
  // db.run("PRAGMA key = \"x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'\"");
//   db.run("PRAGMA CIPHER = 'aes-128-cbc'");
  db.run("CREATE TABLE messages(id INTEGER, user VARCHAR, msg TEXT)");
  db.run("CREATE VIRTUAL TABLE messages_fts USING FTS4(user VARCHAR, msg TEXT)");

  stmt = db.prepare("INSERT INTO messages(id, user, msg) VALUES (?, ?, ?)");
  messages = [
    [1, 'coolaj86', 'this is test message number one']
  , [2, 'ajthedj', 'this is test message number two']
  , [3, 'coolaj86', 'this is test message number three']
  ];
  messages.forEach(function (msg) {
    stmt.run(msg);
  });
  stmt.finalize();

  db.run("INSERT INTO messages_fts SELECT user, msg FROM messages");
  db.get("SELECT * FROM messages INNER JOIN messages_fts ON messages.user = messages_fts.user WHERE messages_fts.msg MATCH 'one'", function (err, data) {
    if (err) {
      console.error(err);
      return;
    }

    console.log(data);
  });
  db.all("SELECT * FROM messages INNER JOIN messages_fts ON messages.user = messages_fts.user WHERE messages_fts.msg MATCH 'two'", function (err, data) {
    if (err) {
      console.error(err);
      return;
    }

    console.log(data);
  });
  db.each("SELECT * FROM messages INNER JOIN messages_fts ON messages.user = messages_fts.user WHERE messages_fts.msg MATCH 'message'", function (err, data) {
    if (err) {
      console.error(err);
      return;
    }

    console.log(data);
  });
});

执行 node test-sqlcipher-fts.js. 如果出现以下画面, 就说明编译成功了.

$ node test-sqlcipher-fts.js
{ id: 1,
  user: 'coolaj86',
  msg: 'this is test message number one' }
[ { id: 2,
    user: 'ajthedj',
    msg: 'this is test message number two' } ]
{ id: 1,
  user: 'coolaj86',
  msg: 'this is test message number one' }
{ id: 1,
  user: 'coolaj86',
  msg: 'this is test message number three' }
{ id: 2,
  user: 'ajthedj',
  msg: 'this is test message number two' }
{ id: 3,
  user: 'coolaj86',
  msg: 'this is test message number one' }
{ id: 3,
  user: 'coolaj86',
  msg: 'this is test message number three' }
  
$ hexdump -C test.sqlcipher | head -15
00000000  8f 47 28 8d cc 91 72 15  8c fd c6 11 57 41 99 16  |.G(...r.....WA..|
00000010  bf 15 20 d4 65 b1 17 1c  73 30 ae 43 fd 31 9e 0c  |.. .e...s0.C.1..|
00000020  c3 5c dc e1 a6 2e 80 b1  3b 97 d9 ed fe dc ea f3  |.\......;.......|
00000030  a8 de ad 04 4b 73 cf ad  01 74 f0 c7 19 71 d3 07  |....Ks...t...q..|
00000040  a3 4f fa 88 ce 00 f1 53  15 dd 06 1b e0 e7 94 50  |.O.....S.......P|
00000050  dd 44 a5 8e d9 21 0d 86  f1 7c 37 7e a2 c1 ce a4  |.D...!...|7~....|
00000060  6e d5 54 c3 79 67 0f dd  1c 0f 3a ac c7 1f ad b3  |n.T.yg....:.....|
00000070  75 63 e7 88 d1 9b f3 f0  16 f3 58 6b 5a 59 b1 70  |uc........XkZY.p|
00000080  c1 77 30 29 fa b4 ef 42  f5 88 57 3f 4c a9 e8 1b  |.w0)...B..W?L...|
00000090  ba d3 aa cb b4 40 a1 0b  53 23 48 92 d1 25 a7 7a  |.....@..S#H..%.z|
000000a0  60 ea 65 b0 56 ea 25 7f  6e cb 74 c5 71 a4 83 df  |`.e.V.%.n.t.q...|
000000b0  52 83 a5 82 0e f7 6b 0f  33 85 16 85 9b 08 2e 93  |R.....k.3.......|
000000c0  23 e0 75 bc 09 bf 76 ca  9a cf e6 8d bf 65 82 0b  |#.u...v......e..|
000000d0  de 68 4e d9 77 0b 5b d4  23 b2 b9 56 7e c5 5c a5  |.hN.w.[.#..V~.\.|
000000e0  c7 a5 75 cc 9b 85 f4 64  ca 47 f9 e3 54 ca 20 c6  |..u....d.G..T. .|

这个时候就可以看出来, 数据库已经被加密了.

错误

  1. LNK2001 无法解析的外部符号 imp _endthreadex

    缺少了 Windows 多线程的库 msvcrt.lib

  2. LNK2001 无法解析的外部符号 __imp__CertOpenStore@20

    因为 OpenSSL 使用了 Windows 的一个加密库: crypt32ws2_32. 加上就可以了.

  3. LINK : fatal error C1007: 无法识别的标志“-Ot”(在“p2”中)

    在 Windows 系统上, SQLCipher 的静态库是我们自己编译的. 我遇到这个错误的原因是我使用的是 VS2017 编译的静态库, 但是连接的工具缺失 vs 2015. 将 SQLCipher 的工具版本降级就可以了.

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

推荐阅读更多精彩内容