C语言如何集成SQLcipher实现数据库加密?

99ANYc3cd6
预计阅读时长 30 分钟
位置: 首页 C语言 正文

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

c语言 sqlcipher
(图片来源网络,侵删)

对于 C 语言开发者来说,这意味着你需要:

  1. 下载并编译 SQLCipher 库(而不是标准的 SQLite)。
  2. 链接 SQLCipher 的库文件 到你的 C 项目中。
  3. 使用 SQLCipher 提供的 C API 来设置密码和操作数据库。

核心概念

  1. 透明加密:对开发者是透明的,你仍然使用 sqlite3_open, sqlite3_exec, sqlite3_prepare_v2 等熟悉的函数,加密/解密在底层自动完成。
  2. 密钥:这是一个密码字符串,SQLCipher 会用它派生出加密所需的密钥。
  3. PRAGMA 指令:这是与 SQLCipher 交互的关键,你通过标准的 SQLite PRAGMA 命令来控制加密行为。
    • key('your_password'): 设置或更改数据库的加密密钥,这是最核心的指令。
    • rekey('new_password'): 更改数据库的密钥,这会先解密整个数据库,然后用新密钥重新加密。
    • cipher_default_kdf_iter: 控制密钥派生函数的迭代次数,增加安全性(默认值可能较低,建议在生产环境中调高)。

环境准备:编译 SQLCipher C 库

你不能直接使用标准的 libsqlite3.sosqlite3.dll,你必须下载并编译 SQLCipher 源码。

在 Linux (Ubuntu/Debian) 上

  1. 安装依赖:

    sudo apt-get update
    sudo apt-get install build-essential tclsh libssl-dev
  2. 下载并编译 SQLCipher:

    c语言 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
  3. 验证安装:

    # 检查头文件和库文件是否在标准位置
    ls /usr/local/include/sqlite3.h  # 应该存在
    ls /usr/local/lib/libsqlcipher.a # 应该存在

在 Windows (Visual Studio) 上

这个过程稍微复杂一些,通常需要使用 CMake。

  1. 安装依赖:

    • Visual Studio: 安装 Visual Studio(Community 版本),并确保安装了“使用 C++ 的桌面开发”工作负载。
    • OpenSSL: 从 OpenSSL 官网 下载并安装 Win32 或 Win64 版本的 OpenSSL,记住安装路径,C:\Program Files\OpenSSL-Win64
    • CMake: 从 CMake 官网 下载并安装 CMake。
  2. 使用 CMake 编译 SQLCipher:

    c语言 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.libsqlite3.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)

  1. 创建一个新的 C++ 空项目(即使代码是 C,用 C++ 项目链接更简单)。
  2. 将上面的代码保存为 sqlcipher_app.c 并添加到项目中。
  3. 在项目属性中配置:
    • C/C++ -> 常规 -> 附加包含目录: 添加你编译 SQLCipher 时的路径,C:\sqlcipher-install\includesqlcipher-build\Release
    • 链接器 -> 常规 -> 附加库目录: 添加 C:\sqlcipher-install\libsqlcipher-build\Release
    • 链接器 -> 输入 -> 附加依赖项: 添加 sqlcipher.lib
    • 链接器 -> 输入 -> 附加依赖项: 添加 libssl.liblibcrypto.lib(你从 OpenSSL 安装目录中找到的)。
  4. 编译并运行项目。

关键点与最佳实践

  1. 错误处理: 每一个 SQLite/SQLCipher API 调用后都应该检查返回码 rc,并在出错时使用 sqlite3_errmsg(db) 获取错误信息。
  2. 密钥管理: 这是安全中最重要也最困难的一环。不要将硬编码的密码写在你的源代码中,密钥应该从安全的地方获取,
    • 操作系统的密钥环/钥匙串。
    • 环境变量。
    • 由用户在首次运行时输入。
    • 从安全的网络服务器获取。
  3. 提高安全性 (KDF Iterations): 默认的密钥派生迭代次数可能不足以抵御暴力破解,你可以在设置密钥之前调整它。
    // 在 sqlite3_key 之前执行
    sqlite3_exec(db, "PRAGMA cipher_default_kdf_iter = 64000;", NULL, NULL, &zErrMsg);

    更高的值(如 100,000 或更高)会更安全,但也会打开数据库的速度变慢,你需要根据安全需求和性能进行权衡。

  4. 混淆二进制文件: 对于桌面应用,即使你保护好了密钥,逆向工程师仍然可能从你的二进制文件中找到它,可以考虑使用工具(如 UPX)或混淆技术来增加逆向工程的难度。
  5. 内存安全: 密钥在内存中以明文形式存在,当不再需要时,考虑用安全的内存清除函数(如 memset_s 或 OpenSSL 的 OPENSSL_cleanse)将其从内存中擦除。

在 C 语言中使用 SQLCipher 的核心流程是:

  1. 准备环境: 编译或获取预编译的 SQLCipher C 库(libsqlcipher)。
  2. 链接项目: 在你的 C 项目中正确包含头文件并链接 SQLCipher 和 OpenSSL 库。
  3. 设置密钥: 在 sqlite3_open 之后,立即调用 sqlite3_key(db, password, len) 来解锁数据库。
  4. 正常操作: 之后,所有标准的 SQLite 操作(exec, prepare, step 等)都可以照常使用,SQLCipher 会自动处理加解密。
  5. 管理密钥: 使用 sqlite3_rekey 来更改密码,并妥善管理密钥的存储。

通过这种方式,你可以轻松地为你的 C 应用程序增加强大的数据加密功能。

-- 展开阅读全文 --
头像
C语言duplicate是什么?如何解决?
« 上一篇 今天
dede如何按关键字实现精准内容检索?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]