在 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 的工具版本降级就可以了.

    原文作者:l蓝色梦幻
    原文地址: https://www.jianshu.com/p/68bb0b066a77
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞