SQLCipher#002#SQLCipher的下载、编译、使用和测试程序 -- Windows版

上文我们介绍了基于Linux系统的SQLCipher的基本使用方法,本文介绍基于Windows的编译、使用和测试等基本操作。

1/6) 下载源代码

官方网站:https://www.zetetic.net/sqlcipher
代码网站:https://github.com/sqlcipher/sqlcipher
下载网址:https://github.com/sqlcipher/sqlcipher/releases
演示版本:https://github.com/sqlcipher/sqlcipher/archive/refs/tags/v4.10.0.tar.gz

使用上述链接下载代码包之后,解压如下图所示:

2/6) 安装依赖

SQLCipher的加密功能依赖于OpenSSL,我们可以自行编译OpenSSL或使用预编译库。本文采用slproweb.com提供的预编译库。

下载网址:https://slproweb.com/products/Win32OpenSSL.html
演示版本:https://slproweb.com/download/Win64OpenSSL-3_5_4.exe

如需OpenSSL的其他版本,打开下载网址后,可以在页面上查找特定版本,如下图所示:

下载完毕后,双击直接运行,在如下界面选择OpenSSL库的存储路径,该路径后续我们需要使用到。如下图所示:

安装完毕后,如下图所示:

3/6) 编译和安装

第一步,进入Visual Studiod的“开发人员工具命令提示命令”对话框。

在Windows上编译SQLCipher,需要使用Visual Studio自带的“适用于 VS 2017 的 x64 本机工具命令提示”进行编译,打开方法如下图所示:

第二步,使用如下命令生成sqlite3.c文件:

1
2
nmake /f Makefile.msc clean
nmake /f Makefile.msc sqlite3.c

命令执行过程如下图所示:

第三步,sqlite3.c生成之后,使用如下命令编译基于OpenSSL动态库的SQLCipher动态库

1
2
3
4
5
6
# 注意修改其中的 OpenSSL 的安装路径,编译动态库
cl -I"D:\dev\sdk\openssl\3.5.4\include" sqlite3.c ^
-DSQLITE_API=__declspec(dllexport) -DSQLITE_TEMP_STORE=2 -DSQLITE_HAS_CODEC -DSQLITE_EXTRA_INIT=sqlcipher_extra_init ^
-DSQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown -DHAVE_STDINT_H /MD -link -dll ^
-out:sqlcipher.dll ^
-LIBPATH:"D:\dev\sdk\openssl\3.5.4\lib\VC\x64\MD" libcrypto.lib advapi32.lib user32.lib

该命令执行过程,如下图所示:

编译完毕之后,我们可以在当前目录下找到动态库sqlcipher.dll和导入库sqlcipher.lib,如下图所示:

第四步,安装库文件和头文件

由于没有提供安装功能,我们需要自行拷贝所需文件制作一个开发用的SDK。过程如下:

1
2
3
4
5
6
7
$1:创建目录 D:\dev\sdk\sqlciper\4.10.0
$2:创建目录 D:\dev\sdk\sqlciper\4.10.0\include
$3:创建目录 D:\dev\sdk\sqlciper\4.10.0\bin
$4:创建目录 D:\dev\sdk\sqlciper\4.10.0\lib
$5:将D:\dev\sqlcipher-4.10.0目录下的 sqlite3.h 和 sqlite3ext.h 拷贝到 D:\dev\sdk\sqlciper\4.10.0\include
$6:将D:\dev\sqlcipher-4.10.0目录下的 sqlcipher.dll 和 sqlcipher.exp 拷贝到 D:\dev\sdk\sqlciper\4.10.0\bin
$7:将D:\dev\sqlcipher-4.10.0目录下的 sqlcipher.lib 拷贝到 D:\dev\sdk\sqlciper\4.10.0\lib

最终其目录结构如下图所示:

至此我们已经成功编译了SQLCipher的动态库,后续我们演示下基于该SDK进行开发的过程。

4/6) 使用SQLCipher库编码开发

如下测试代码,完成打开数据库、设置密钥、创建数据表、插入数据、检索数据等基本操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sqlite3.h>
#include <string>

#define TEST_DB "test.db"
#define TEST_KEY "123456789"

sqlite3* open_database(const char *db_path, const char *key)
{
sqlite3 *db = NULL;
int rc = sqlite3_open(db_path, &db);

if (rc != SQLITE_OK)
{
fprintf(stderr, "无法打开数据库 %s: %s\n", db_path, sqlite3_errmsg(db));
sqlite3_close(db);
return NULL;
}

rc = sqlite3_key(db, key, strlen(key));
if (rc != SQLITE_OK)
{
fprintf(stderr, "设置密钥失败: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return NULL;
}

return db;
}

void close_database(sqlite3 *db)
{
if (db)
{
sqlite3_close(db);
}
}

int execute_sql(sqlite3 *db, const char *sql)
{
char *err_msg = NULL;
int rc = sqlite3_exec(db, sql, NULL, NULL, &err_msg);

if (rc != SQLITE_OK)
{
fprintf(stderr, "SQL执行失败: %s\nSQL语句: %s\n", err_msg, sql);
sqlite3_free(err_msg);
}

return rc;
}

int test_basic_operations()
{
sqlite3 * db = open_database(TEST_DB, TEST_KEY);
if (!db)
{
fprintf(stderr, "无法打开数据库\n");
return 0;
}

// 创建表
const char *create_table_sql = "CREATE TABLE IF NOT EXISTS msg ("
"id interger PRIMARY KEY, name text NOT NULL, age interger, school text)";

if (execute_sql(db, create_table_sql) != SQLITE_OK)
{
fprintf(stderr, "创建表失败\n");
close_database(db);
return 0;
}

// 插入数据
const char *insert_sql = "INSERT INTO msg(id, name, age, school) VALUES(20250102, 'lishi', 19, 'pku')";
if (execute_sql(db, insert_sql) != SQLITE_OK)
{
fprintf(stderr, "插入数据失败\n");
close_database(db);
return 0;
}

// 查询数据
sqlite3_stmt *stmt;
const char *query_sql = "SELECT id, name, age, school FROM msg";
if (sqlite3_prepare_v2(db, query_sql, -1, &stmt, NULL) != SQLITE_OK)
{
fprintf(stderr, "查询准备失败: %s\n", sqlite3_errmsg(db));
close_database(db);
return 0;
}

while (sqlite3_step(stmt) == SQLITE_ROW)
{
int id = sqlite3_column_int(stmt, 0);
const char *name = (const char *)sqlite3_column_text(stmt, 1);
int age = sqlite3_column_int(stmt, 2);
const char *school = (const char *)sqlite3_column_text(stmt, 3);

printf("查询结果: ID=%d, Name=%s, Age=%d, School=%s\n", id, name, age, school);
}

sqlite3_finalize(stmt);

// 关闭数据库
close_database(db);

return 1;
}

int main()
{
test_basic_operations();

return 0;
}

在Visual Studio设置好项目属性之后,编译上述源代码,一般会报如下错误:

1
2
3
严重性	代码	说明	项目	文件	行	禁止显示状态
错误(活动) E0020 未定义标识符 "sqlite3_key" sqlcipher D:\dev\example\sqlcipher\sqlcipher\sqlcipher.cpp 23
错误 C3861 “sqlite3_key”: 找不到标识符 sqlcipher d:\dev\example\sqlcipher\sqlcipher\sqlcipher.cpp 23

要解决上述问题,我们需要在项目属性“预处理器定义”加上编译参数“SQLITE_HAS_CODEC”,添加方法如下图所示:

编译参数添加完毕后,我们继续运行该测试代码,结果如下图所示:

至此,我们演示了SQLCipher的下载、编译、安装、命令行、编码开发等基本操作,整体来看开发人员很容易从SQLite迁移到SQLCipher。

使用该SDK的测试代码和工程文件,可参考:https://github.com/wosanxian/sqlcipher-example/tree/main/example/win.shared.openssl_shared

5/5) 其他编译方法示例

上述步骤中,我们演示了基于OpenSSL动态库编译SQLCipher动态库的方法,下面描述其他编译方法。

  • 基于OpenSSL静态库编译SQLCipher动态库的方法(后续部署时仅发布sqlcipher.dll即可,libcrypto-3-x64.dll无需一起发布),使用如下命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 第1步:生成sqlite3.c文件:
nmake /f Makefile.msc clean
nmake /f Makefile.msc sqlite3.c

# 第2步:编译动态库,注意修改其中的 OpenSSL 的安装路径
cl -I"D:\dev\sdk\openssl\3.5.4\include" sqlite3.c ^
-DSQLITE_API=__declspec(dllexport) -DSQLITE_TEMP_STORE=2 -DSQLITE_HAS_CODEC -DSQLITE_EXTRA_INIT=sqlcipher_extra_init ^
-DSQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown -DHAVE_STDINT_H /MD -link -dll ^
-out:sqlcipher.dll ^
-LIBPATH:"D:\dev\sdk\openssl\3.5.4\lib\VC\x64\MD" libcrypto_static.lib advapi32.lib user32.lib ws2_32.lib Crypt32.lib

# 第3步:部署方法:
$1:创建目录 D:\dev\sdk\sqlciper\4.10.0
$2:创建目录 D:\dev\sdk\sqlciper\4.10.0\include
$3:创建目录 D:\dev\sdk\sqlciper\4.10.0\bin
$4:创建目录 D:\dev\sdk\sqlciper\4.10.0\lib
$5:将D:\dev\sqlcipher-4.10.0目录下的 sqlite3.h 和 sqlite3ext.h 拷贝到 D:\dev\sdk\sqlciper\4.10.0\include
$6:将D:\dev\sqlcipher-4.10.0目录下的 sqlcipher.dll 和 sqlcipher.exp 拷贝到 D:\dev\sdk\sqlciper\4.10.0\bin
$7:将D:\dev\sqlcipher-4.10.0目录下的 sqlcipher.lib 拷贝到 D:\dev\sdk\sqlciper\4.10.0\lib

使用该SDK的测试代码和工程文件,可参考:https://github.com/wosanxian/sqlcipher-example/tree/main/example/win.shared.openssl_static

  • 编译SQLCipher静态库的方法(开发时仅依赖sqlcipher_static.lib和libcrypto_static.lib,无需发布sqlcipher.dll和libcrypto-3-x64.dll):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第1步:生成sqlite3.c文件:
nmake /f Makefile.msc clean
nmake /f Makefile.msc sqlite3.c

# 第2步:编译静态库,注意修改其中的 OpenSSL 的安装路径:
cl /c -I"D:\dev\sdk\openssl\3.5.4\include" sqlite3.c ^
-DSQLITE_API=__declspec(dllexport) -DSQLITE_TEMP_STORE=2 -DSQLITE_HAS_CODEC -DSQLITE_EXTRA_INIT=sqlcipher_extra_init ^
-DSQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown -DHAVE_STDINT_H /MD

lib.exe /OUT:sqlcipher_static.lib sqlite3.obj

# 第3步:部署方法:
$1:创建目录 D:\dev\sdk\sqlciper\4.10.0
$2:创建目录 D:\dev\sdk\sqlciper\4.10.0\include
$3:创建目录 D:\dev\sdk\sqlciper\4.10.0\lib
$4:将D:\dev\sqlcipher-4.10.0目录下的 sqlite3.h 和 sqlite3ext.h 拷贝到 D:\dev\sdk\sqlciper\4.10.0\include
$5:将D:\dev\sqlcipher-4.10.0目录下的 sqlcipher_static.lib 拷贝到 D:\dev\sdk\sqlciper\4.10.0\lib

使用该SDK的测试代码和工程文件,可参考:https://github.com/wosanxian/sqlcipher-example/tree/main/example/win.static.openssl_static

  • 编译SQLCipher数据库管理工具sqlcipher.exe的方法如下:
1
2
3
4
5
6
7
8
9
10
# 生成sqlite3.c文件:
nmake /f Makefile.msc clean
nmake /f Makefile.msc sqlite3.c

# 编译数据库管理工具,注意修改其中的 OpenSSL 的安装路径
cl -I"D:\dev\sdk\openssl\3.5.4\include" sqlite3.c shell.c ^
-DSQLITE_TEMP_STORE=2 -DSQLITE_HAS_CODEC -DSQLITE_OS_WIN -DSQLITE_EXTRA_INIT=sqlcipher_extra_init ^
-DSQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown -DHAVE_STDINT_H /MT -link ^
-out:sqlcipher.exe ^
-LIBPATH:"D:\dev\sdk\openssl\3.5.4\lib\VC\x64\MD" libcrypto.lib advapi32.lib user32.lib gdi32.lib

6/6) Windows上浏览加密数据库的工具

除可以使用sqlciper.exe管理加密数据库之外,我们还可以使用可视化工具DB Browser fro SQLite,完成同样的操作,并且它更加直观和简单。

下载地址:https://sqlitebrowser.org/dl
演示版本:https://github.com/sqlitebrowser/sqlitebrowser/releases/download/v3.13.1/DB.Browser.for.SQLite-v3.13.1-win64.zip

下载后解压如下图所示:

双击 DB Browser for SQLCipher.exe 即可运行程序,然后选择打开数据库,按照要求输入数据库原密码之后,打开数据库:

如果我们输入的密码正确,接下来就可以正常的访问数据了,如下图所示: