SQLite文件加密完整教程:从零学会SQLCipher全库加密(C++实战)

导读 :原生 SQLite 落盘是明文,拿到 .db 就能打开。SQLCipher / sqlite3mc 可以在打开连接后立刻设密钥,新建库和旧明文库都能加密。本文按原文把时序、C++ 代码、sqlcipher_export 迁移和 Navicat 建库走一遍,并把 file is not a databasesqlite3_key 未定义的常见坑列出来。


前言

做桌面客户端、嵌入式设备、轻量化后端开发的小伙伴,大概率都踩过 SQLite 的安全大坑:

原生 SQLite 数据库完全明文存储,只要别人拿到你的 .db 文件,无需密码、无需破解,直接用数据库工具就能查看所有数据,用户隐私、业务数据、核心配置全部裸奔。

很多人做SQLite文件加密时,都会被一个误区误导:数据库必须创建时加密,已有的明文数据库文件无法后期加密

今天这篇文章,我结合多年项目实战经验,彻底破除这个误区,手把手带你实现:新建库即时加密 + 旧明文数据库无损转加密,附带完整可编译的 C++ 代码、以及实战可落地生产!


一、核心误区辟谣(重中之重)

先直接抛出颠覆新手认知的核心结论

SQLCipher 不需要在创建数据库时加密!

✅ 支持两种场景全覆盖:

  1. 新数据库:打开空库后立即设置密钥,后续所有数据自动加密落盘
  2. 旧明文数据库:支持无损迁移,一键将明文 db 转为加密 db,不丢任何表结构、数据、索引

很多人数据库加密失败、报 file is not a database 错误,根本原因不是工具问题,而是操作时序错误

这里给大家划死核心规则:

SQLCipher 加密的本质:密钥只激活当前连接的加密编解码器,仅对「设置密钥之后的IO操作」生效,不会自动加密历史明文数据!

❌ 错误操作:先建表、插数据、读写数据,再设置密钥(数据半明半密,数据库直接损坏)

✅ 正确操作:打开数据库后,第一行代码必须设置密钥,再执行所有业务SQL


二、SQLCipher 加密原理极简科普

SQLCipher 是 SQLite 官方主流开源加密分支,核心优势:

  • 采用 AES-256-CBC 高强度加密,搭配 PBKDF2 密钥派生、HMAC 校验,防破解、防篡改
  • 页级透明加密,业务代码零改动,原生SQL语法完全兼容
  • 自动加密 WAL、日志临时文件,无明文泄露风险
  • 跨平台适配:Windows/Linux/嵌入式/Qt 项目通用

唯一局限:仅保护磁盘静态文件,数据加载到内存后为明文,无法防进程内存抓包,满足绝大多数业务安全需求。


三、C++ 完整实战代码(可直接编译运行)

包含:新建加密库、数据读写、修改密码、异常捕获,纯原生C++。

这里需要依赖"sqlite3mc.lib"

示例代码资源链接:【免费】sqlite文件加密示例代码资源-CSDN下载

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include "sqlite3.h"

// SQL查询回调
static int sqlCallback(void* data, int argc, char** argv, char** azColName)
{
    for (int i = 0; i < argc; i++)
    {
        std::cout << azColName[i] << " = " << (argv[i] ? argv[i] : "NULL") << "\t";
    }
    std::cout << "\n";
    return 0;
}

int main()
{
    sqlite3* db = nullptr;
    char* errMsg = nullptr;
    const char* dbPath = "secure_demo.db";
    const char* pwd = "demo654321"; // 自定义密钥

    // 1. 打开/创建数据库
    int ret = sqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
    if (ret != SQLITE_OK)
    {
        std::cerr << "数据库打开失败:" << sqlite3_errmsg(db) << std::endl;
        sqlite3_close(db);
        return -1;
    }

    //  wxSQLite3 AES-128 CBC,必须在 sqlite3_key 之前指定
    sqlite3_exec(db, "PRAGMA cipher = 'aes128cbc';", nullptr, nullptr, &errMsg);

    // 【核心必写】打开后立即设置密钥,所有业务操作之前执行!
    ret = sqlite3_key(db, pwd, strlen(pwd));
    if (ret != SQLITE_OK)
    {
        std::cerr << "密钥设置失败:" << sqlite3_errmsg(db) << std::endl;
        sqlite3_close(db);
        return -1;
    }

    // 开启WAL模式,提升并发性能,日志文件同步加密
    sqlite3_exec(db, "PRAGMA journal_mode=WAL;", nullptr, nullptr, &errMsg);

    // 2. 建表测试
    const char* createSql = R"(
        CREATE TABLE IF NOT EXISTS user_info(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT,
            phone TEXT
        );
    )";
    sqlite3_exec(db, createSql, nullptr, nullptr, &errMsg);

    // 3. 插入测试数据
    sqlite3_exec(db, "INSERT INTO user_info(username,phone) VALUES('aaaa','13800138000');", nullptr, nullptr, &errMsg);

    // 4. 查询数据
    std::cout << "===== 加密数据库查询结果 =====\n";
    sqlite3_exec(db, "SELECT * FROM user_info;", sqlCallback, nullptr, &errMsg);

    // 5. 修改数据库密码(rekey 仅适用于已加密数据库)
    //const char* newPwd = "demo123456";
    //ret = sqlite3_rekey(db, newPwd, strlen(newPwd));
    //if (ret == SQLITE_OK)
    //{
    //    std::cout << "\n✅ 密码修改成功!新密码:" << newPwd << std::endl;
    //}

    // 关闭数据库
    sqlite3_close(db);
    std::cout << "\n✅ 加密流程执行完成\n";
    return 0;
}

四、旧明文数据库 → 加密数据库无损转换

针对已有存量明文 SQLite 数据库,无需删库重建,通过 sqlcipher_export 一键全量迁移,零数据丢失:

cpp 复制代码
// 打开旧明文数据库
sqlite3* plainDb = nullptr;
char* errMsg = nullptr;
sqlite3_open("old_plain.db", &plainDb);

// 新建加密数据库并设置密钥
sqlite3* encDb = nullptr;
sqlite3_open("new_secure.db", &encDb);

sqlite3_exec(db, "PRAGMA cipher = 'aes128cbc';", nullptr, nullptr, &errMsg);
sqlite3_key(encDb, "YourPwd@123", 11);

char* err = nullptr;
// 全量迁移:表、数据、索引、触发器全部复制
const char* transferSql = "ATTACH DATABASE 'old_plain.db' AS plain KEY ''; SELECT sqlcipher_export('plain'); DETACH DATABASE plain;";
sqlite3_exec(encDb, transferSql, nullptr, nullptr, &err);

执行完成后,new_secure.db 即为完整加密数据库,原明文数据库可备份删除。


五、加密库的另外创建方式,可以依赖第三方工具创建

这里可以依赖Navicat软件进行创建,如下图所示:


六、常见问题汇总

1、报错:file is not a database

可能原因有如下几种:

1.1、密码错误;

1.2、SQLCipher版本不兼容;

1.3、创建表的时候先写数据后加密;

2、报错:sqlite3_key 未定义

原因:链接了系统原生 SQLite 库,未使用编译后的 SQLCipher 库

相关推荐
bluesky9612221 小时前
malloc 和 realloc C/C++
c语言·c++·算法
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<二十三>:图像平滑之非线性滤波
c++·人工智能·opencv·计算机视觉
牛油果子哥q1 小时前
C++模板万字深度精讲:函数/类模板特化、全特化与偏特化、SFINAE机制、类型萃取、模板元编程入门、工程实战落地
开发语言·c++·面试
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<二十一>:灰度直方图清晰化图像
c++·人工智能·opencv·计算机视觉
神仙别闹2 小时前
基于C++ 实现 L 系统分形树
开发语言·c++
INGNIGHT2 小时前
1584.连接所有点的最小费用(最小生成树&并查集union find)
c++·leetcode
wabs6662 小时前
关于二叉树【力扣144.二叉树的前序遍历的思考】
数据结构·c++·算法·leetcode·二叉树
zmzb01033 小时前
C++课后习题训练记录Day199
开发语言·c++
人邮异步社区3 小时前
如何系统地学习 C++ 语言?
开发语言·c++·学习