经过试验, 使用 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
注意:
当 lib 中存在有动态库的时候, 默认会安装动态库. 但是我们需要连接静态库. 这时候删除动态库就可以了. 但是 sqlcipher 的静态库依赖于 openssl 的静态库, 因此需要将 openssl 的库也放到 yourlib 中, 并指定名称.
由于默认的 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. .|
这个时候就可以看出来, 数据库已经被加密了.
错误
LNK2001 无法解析的外部符号 imp _endthreadex
缺少了 Windows 多线程的库
msvcrt.lib
LNK2001 无法解析的外部符号 __imp__CertOpenStore@20
因为 OpenSSL 使用了 Windows 的一个加密库:
crypt32
与ws2_32
. 加上就可以了.LINK : fatal error C1007: 无法识别的标志“-Ot”(在“p2”中)
在 Windows 系统上, SQLCipher 的静态库是我们自己编译的. 我遇到这个错误的原因是我使用的是 VS2017 编译的静态库, 但是连接的工具缺失 vs 2015. 将 SQLCipher 的工具版本降级就可以了.