SQLCipher 是一个在 SQLite 基础上扩展的开源库,它通过 256 位 AES 加密算法对整个数据库文件进行透明加密,这意味着你无需修改你的 SQL 语句,只需要在打开数据库时提供一个密码,后续的所有读写操作都会自动进行加密和解密。

(图片来源网络,侵删)
对于 C 语言开发者来说,这意味着你需要:
- 下载并编译 SQLCipher 库(而不是标准的 SQLite)。
- 链接 SQLCipher 的库文件 到你的 C 项目中。
- 使用 SQLCipher 提供的 C API 来设置密码和操作数据库。
核心概念
- 透明加密:对开发者是透明的,你仍然使用
sqlite3_open,sqlite3_exec,sqlite3_prepare_v2等熟悉的函数,加密/解密在底层自动完成。 - 密钥:这是一个密码字符串,SQLCipher 会用它派生出加密所需的密钥。
- PRAGMA 指令:这是与 SQLCipher 交互的关键,你通过标准的 SQLite
PRAGMA命令来控制加密行为。key('your_password'): 设置或更改数据库的加密密钥,这是最核心的指令。rekey('new_password'): 更改数据库的密钥,这会先解密整个数据库,然后用新密钥重新加密。cipher_default_kdf_iter: 控制密钥派生函数的迭代次数,增加安全性(默认值可能较低,建议在生产环境中调高)。
环境准备:编译 SQLCipher C 库
你不能直接使用标准的 libsqlite3.so 或 sqlite3.dll,你必须下载并编译 SQLCipher 源码。
在 Linux (Ubuntu/Debian) 上
-
安装依赖:
sudo apt-get update sudo apt-get install build-essential tclsh libssl-dev
-
下载并编译 SQLCipher:
(图片来源网络,侵删)# 前往 SQLCipher 官网下载最新源码压缩包 # 下载 4.5.3 版本 wget https://www.zetetic.net/sqlcipher/download/?f=sqlcipher-4.5.3.tar.gz tar -xvf sqlcipher-*.tar.gz cd sqlcipher-* # 配置,指定 OpenSSL 路径 # 注意:--enable-tempstore=always 是一个好习惯,确保内存模式可用 ./configure --enable-tempstore=always --with-crypto-lib=openssl # 编译和安装 make sudo make install
-
验证安装:
# 检查头文件和库文件是否在标准位置 ls /usr/local/include/sqlite3.h # 应该存在 ls /usr/local/lib/libsqlcipher.a # 应该存在
在 Windows (Visual Studio) 上
这个过程稍微复杂一些,通常需要使用 CMake。
-
安装依赖:
- Visual Studio: 安装 Visual Studio(Community 版本),并确保安装了“使用 C++ 的桌面开发”工作负载。
- OpenSSL: 从 OpenSSL 官网 下载并安装 Win32 或 Win64 版本的 OpenSSL,记住安装路径,
C:\Program Files\OpenSSL-Win64。 - CMake: 从 CMake 官网 下载并安装 CMake。
-
使用 CMake 编译 SQLCipher:
(图片来源网络,侵删)# 1. 打开 "x64 Native Tools Command Prompt for VS" 终端 # 2. 下载并解压 SQLCipher 源码 # 3. 创建一个用于构建的目录 mkdir sqlcipher-build cd sqlcipher-build # 4. 运行 CMake 配置 # 将 <path_to_openssl> 替换为你实际的 OpenSSL 路径 cmake -G "Visual Studio 17 2025" -A x64 ` -DCMAKE_INSTALL_PREFIX=C:\sqlcipher-install ` -DOPENSSL_ROOT_DIR=<path_to_openssl> ` ..\path_to_sqlcipher_source # 5. 编译 cmake --build . --config Release # 6. 安装 (可选,会将头文件和库文件复制到你指定的目录) cmake --install . --config Release编译成功后,你会在
sqlcipher-build目录下找到Release文件夹,里面有sqlcipher.lib和sqlite3.h等文件。
C 语言编程示例
下面是一个完整的 C 语言示例,展示了如何创建一个加密数据库、插入数据、查询数据,以及如何重新设置密钥。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 必须包含 SQLCipher 的头文件
#include <sqlite3.h>
// 回调函数,用于处理 sqlite3_exec 的查询结果
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for (i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main() {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
// 1. 打开数据库文件
// 如果文件不存在,SQLCipher 会创建它
rc = sqlite3_open("test_encrypted.db", &db);
if (rc) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
return(1);
}
fprintf(stderr, "数据库已成功打开,\n");
// 2. 设置数据库密钥
// 这是加密的核心步骤!
// 使用 PRAGMA key('your-secret-password');
rc = sqlite3_key(db, "your-secret-password", strlen("your-secret-password"));
// 注意:一些旧版本的 SQLCipher 可能使用 sqlite3_key(db, "your-secret-password");
// 或者通过 PRAGMA 语句:rc = sqlite3_exec(db, "PRAGMA key = 'your-secret-password';", NULL, NULL, &zErrMsg);
// 现代 SQLCipher 推荐直接使用 sqlite3_key API。
if (rc != SQLITE_OK) {
fprintf(stderr, "设置密钥失败: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return(1);
}
fprintf(stderr, "数据库密钥已设置,\n");
// 3. 创建一个表 (如果不存在)
// 第一次执行时,数据库文件会被加密
const char *sql = "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY,"
"name TEXT NOT NULL,"
"email TEXT NOT NULL);";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL 错误: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return(1);
}
fprintf(stderr, "表 'users' 已创建或已存在,\n");
// 4. 插入数据
sql = "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');"
"INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_ok) {
fprintf(stderr, "插入数据失败: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stderr, "数据插入成功,\n");
}
// 5. 查询数据
sql = "SELECT * FROM users;";
fprintf(stderr, "--- 查询结果 ---\n");
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "查询失败: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
// 6. 重新设置密钥 (修改密码)
fprintf(stderr, "--- 正在重新设置密钥... ---\n");
rc = sqlite3_rekey(db, "new-super-secret-password", strlen("new-super-secret-password"));
if (rc != SQLITE_OK) {
fprintf(stderr, "重新设置密钥失败: %s\n", sqlite3_errmsg(db));
} else {
fprintf(stderr, "数据库密钥已成功更改为 'new-super-secret-password',\n");
}
// 7. 关闭数据库
sqlite3_close(db);
fprintf(stderr, "数据库已关闭,\n");
// 8. 验证加密
// 尝试用错误的密码打开数据库
fprintf(stderr, "--- 尝试用错误密码打开数据库 ---\n");
rc = sqlite3_open("test_encrypted.db", &db);
if (rc) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
return(1);
}
// 用旧密码尝试
sqlite3_key(db, "your-secret-password", strlen("your-secret-password"));
// 尝试执行一个查询
rc = sqlite3_exec(db, "SELECT count(*) FROM users;", callback, 0, &zErrMsg);
if (rc == SQLITE_ERROR) { // 错误码是 SQLITE_ERROR,不是 SQLITE_OK
fprintf(stderr, "成功!用旧密码无法访问数据库,错误信息: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stderr, "警告!用旧密码仍然可以访问数据库,加密可能失败,\n");
}
sqlite3_close(db);
return 0;
}
如何编译上面的代码 (Linux)
# 假设你按照前面的步骤将 SQLCipher 安装在 /usr/local # -lsqlcipher 告诉链接器链接 sqlcipher 库 # -lcrypto 是因为 SQLCipher 依赖 OpenSSL 的加密库 gcc -o sqlcipher_app sqlcipher_app.c -I/usr/local/include -L/usr/local/lib -lsqlcipher -lcrypto # 运行程序 ./sqlcipher_app
如何编译上面的代码 (Windows, Visual Studio)
- 创建一个新的 C++ 空项目(即使代码是 C,用 C++ 项目链接更简单)。
- 将上面的代码保存为
sqlcipher_app.c并添加到项目中。 - 在项目属性中配置:
- C/C++ -> 常规 -> 附加包含目录: 添加你编译 SQLCipher 时的路径,
C:\sqlcipher-install\include或sqlcipher-build\Release。 - 链接器 -> 常规 -> 附加库目录: 添加
C:\sqlcipher-install\lib或sqlcipher-build\Release。 - 链接器 -> 输入 -> 附加依赖项: 添加
sqlcipher.lib。 - 链接器 -> 输入 -> 附加依赖项: 添加
libssl.lib和libcrypto.lib(你从 OpenSSL 安装目录中找到的)。
- C/C++ -> 常规 -> 附加包含目录: 添加你编译 SQLCipher 时的路径,
- 编译并运行项目。
关键点与最佳实践
- 错误处理: 每一个 SQLite/SQLCipher API 调用后都应该检查返回码
rc,并在出错时使用sqlite3_errmsg(db)获取错误信息。 - 密钥管理: 这是安全中最重要也最困难的一环。不要将硬编码的密码写在你的源代码中,密钥应该从安全的地方获取,
- 操作系统的密钥环/钥匙串。
- 环境变量。
- 由用户在首次运行时输入。
- 从安全的网络服务器获取。
- 提高安全性 (KDF Iterations): 默认的密钥派生迭代次数可能不足以抵御暴力破解,你可以在设置密钥之前调整它。
// 在 sqlite3_key 之前执行 sqlite3_exec(db, "PRAGMA cipher_default_kdf_iter = 64000;", NULL, NULL, &zErrMsg);
更高的值(如 100,000 或更高)会更安全,但也会打开数据库的速度变慢,你需要根据安全需求和性能进行权衡。
- 混淆二进制文件: 对于桌面应用,即使你保护好了密钥,逆向工程师仍然可能从你的二进制文件中找到它,可以考虑使用工具(如 UPX)或混淆技术来增加逆向工程的难度。
- 内存安全: 密钥在内存中以明文形式存在,当不再需要时,考虑用安全的内存清除函数(如
memset_s或 OpenSSL 的OPENSSL_cleanse)将其从内存中擦除。
在 C 语言中使用 SQLCipher 的核心流程是:
- 准备环境: 编译或获取预编译的 SQLCipher C 库(
libsqlcipher)。 - 链接项目: 在你的 C 项目中正确包含头文件并链接 SQLCipher 和 OpenSSL 库。
- 设置密钥: 在
sqlite3_open之后,立即调用sqlite3_key(db, password, len)来解锁数据库。 - 正常操作: 之后,所有标准的 SQLite 操作(
exec,prepare,step等)都可以照常使用,SQLCipher 会自动处理加解密。 - 管理密钥: 使用
sqlite3_rekey来更改密码,并妥善管理密钥的存储。
通过这种方式,你可以轻松地为你的 C 应用程序增加强大的数据加密功能。
