主页 > 电脑硬件  > 

使用Qt插件和SQLCipher实现SQLite数据库加密与解密

使用Qt插件和SQLCipher实现SQLite数据库加密与解密

SQLite 作为一种轻量级的数据库,被广泛应用于各种桌面和移动应用中。然而,SQLite 本身并不支持数据加密,这时 SQLCipher 成为一个理想的解决方案。本文将详细介绍如何在 Qt 项目中集成 SQLCipher,实现 SQLite 数据库的加密与解密,包括创建加密数据库、插入数据以及查询数据的完整流程。

目录 简介前置条件项目配置代码实现 创建加密数据库并插入数据读取加密数据库并查询数据 常见问题与解决总结 简介

SQLCipher 是一个开源的扩展,提供了透明的 AES-256 加密功能,使得 SQLite 数据库文件的内容能够被加密和解密。通过将 SQLCipher 与 Qt 结合使用,开发者可以轻松地在 Qt 应用中实现数据加密,确保敏感信息的安全性。

前置条件

在开始之前,请确保您的开发环境满足以下条件:

Qt 开发环境:建议使用 Qt 5 或 Qt 6。SQLCipher 库:需要编译或安装 SQLCipher,并确保其与 Qt 兼容。C++ 基础知识:了解基本的 C++ 和 Qt 编程。 项目配置 1. 安装 SQLCipher

首先,需要在系统中安装 SQLCipher。可以通过以下方式进行安装:

使用包管理器:

Windows:建议使用 vcpkg 安装 SQLCipher。

macOS:

brew install sqlcipher

Linux:

sudo apt-get install sqlcipher

从源代码编译:

访问 SQLCipher GitHub 页面,按照说明进行编译。

2. 配置 Qt 项目

创建一个新的 Qt 控制台应用项目,或在现有项目中进行配置。

在项目的 .pro 文件中添加以下内容,以确保链接 SQLCipher 和 Qt SQL 模块:

QT += core sql CONFIG += console c++11 # 根据实际安装路径配置 SQLCipher 库 INCLUDEPATH += /usr/local/include LIBS += -L/usr/local/lib -lsqlcipher

注意:请根据您的系统和 SQLCipher 的安装路径调整 INCLUDEPATH 和 LIBS。

代码实现

以下是一个完整的 Qt 控制台应用程序示例,演示如何使用 SQLCipher 创建加密数据库、插入数据以及读取数据。

创建加密数据库并插入数据 #include <QtSql> #include <QCoreApplication> #include <QStandardPaths> #include <QDir> #include <QFile> #include <QDebug> // 定义加密密钥 const QString DB_PASSWORD = "pass"; // 定义数据库文件名 const QString DB_FILENAME = "local.db"; // 定义表名和示例数据 const QString TABLE_NAME = "test"; const QList<QPair<int, QString>> SAMPLE_DATA = { {1, "AAA"}, {2, "BBB"}, {3, "CCC"}, {4, "DDD"}, {5, "EEE"}, {6, "FFF"}, {7, "GGG"} }; // 函数声明 bool createAndInsertData(const QString &dbPath); bool readData(const QString &dbPath); int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); // 获取数据库文件路径 QString dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString dbPath = QDir(dir).absoluteFilePath(DB_FILENAME); qDebug() << "DB File Path is:" << dbPath; // 检查数据库文件是否存在 bool dbExists = QFile::exists(dbPath); if (!dbExists) { qDebug() << "数据库不存在,正在创建并插入数据..."; if (!createAndInsertData(dbPath)) { qDebug() << "创建数据库或插入数据失败。"; return -1; } qDebug() << "数据库创建并成功插入数据。"; } else { qDebug() << "数据库已存在,跳过创建步骤。"; } // 读取数据 qDebug() << "正在读取数据库中的数据..."; if (!readData(dbPath)) { qDebug() << "读取数据库数据失败。"; return -1; } qDebug() << "数据读取成功。"; return 0; } /** * @brief 创建加密数据库并插入数据 * @param dbPath 数据库文件路径 * @return 成功返回 true,否则返回 false */ bool createAndInsertData(const QString &dbPath) { // 添加 SQLITECIPHER 驱动 QSqlDatabase db = QSqlDatabase::addDatabase("SQLITECIPHER", "create_connection"); db.setDatabaseName(dbPath); db.setPassword(DB_PASSWORD); db.setConnectOptions("QSQLITE_USE_CIPHER=aes256cbc;"); // 使用 AES-256-CBC 加密 if (!db.open()) { qDebug() << "打开数据库失败(创建):" << db.lastError().text(); QSqlDatabase::removeDatabase("create_connection"); return false; } qDebug() << "数据库已打开,用于创建和插入数据。"; QSqlQuery query(db); // 创建表 QString createTableSQL = QString("CREATE TABLE %1 (id INTEGER PRIMARY KEY, name TEXT);").arg(TABLE_NAME); if (!query.exec(createTableSQL)) { qDebug() << "创建表失败:" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("create_connection"); return false; } qDebug() << "表创建成功。"; // 插入数据 query.prepare(QString("INSERT INTO %1 (id, name) VALUES (:id, :name);").arg(TABLE_NAME)); foreach (const QPair<int, QString> &entry, SAMPLE_DATA) { query.bindValue(":id", entry.first); query.bindValue(":name", entry.second); if (!query.exec()) { qDebug() << "插入数据失败 (" << entry.first << "," << entry.second << "):" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("create_connection"); return false; } } qDebug() << "数据插入成功。"; db.close(); QSqlDatabase::removeDatabase("create_connection"); return true; } 读取加密数据库并查询数据 /** * @brief 读取加密数据库中的数据 * @param dbPath 数据库文件路径 * @return 成功返回 true,否则返回 false */ bool readData(const QString &dbPath) { // 添加 SQLITECIPHER 驱动 QSqlDatabase db = QSqlDatabase::addDatabase("SQLITECIPHER", "create_connection"); db.setDatabaseName(dbPath); db.setPassword(DB_PASSWORD); db.setConnectOptions("QSQLITE_USE_CIPHER=aes256cbc;"); // 使用 AES-256-CBC 加密 if (!db.open()) { qDebug() << "打开数据库失败(读取):" << db.lastError().text(); QSqlDatabase::removeDatabase("read_connection"); return false; } qDebug() << "数据库已打开,用于读取数据。"; QSqlQuery query(db); // 验证 SQLCipher 版本(可选) if (query.exec("PRAGMA cipher_version;")) { if (query.next()) { QString cipher_version = query.value(0).toString(); qDebug() << "SQLCipher 版本:" << cipher_version; } else { qDebug() << "无法获取 SQLCipher 版本。"; } } else { qDebug() << "执行 PRAGMA cipher_version 失败:" << query.lastError().text(); } // 查询数据 QString selectSQL = QString("SELECT id, name FROM %1 ORDER BY id;").arg(TABLE_NAME); if (!query.exec(selectSQL)) { qDebug() << "执行 SELECT 查询失败:" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("read_connection"); return false; } // 读取并输出数据 while (query.next()) { int id = query.value(0).toInt(); QString name = query.value(1).toString(); qDebug() << id << ":" << name; } db.close(); QSqlDatabase::removeDatabase("read_connection"); return true; } 完整代码汇总

将上述两个函数和 main 函数合并,即可得到一个完整的示例程序:

#include <QtSql> #include <QCoreApplication> #include <QStandardPaths> #include <QDir> #include <QFile> #include <QDebug> // 定义加密密钥 const QString DB_PASSWORD = "pass"; // 定义数据库文件名 const QString DB_FILENAME = "local.db"; // 定义表名和示例数据 const QString TABLE_NAME = "test"; const QList<QPair<int, QString>> SAMPLE_DATA = { {1, "AAA"}, {2, "BBB"}, {3, "CCC"}, {4, "DDD"}, {5, "EEE"}, {6, "FFF"}, {7, "GGG"} }; // 函数声明 bool createAndInsertData(const QString &dbPath); bool readData(const QString &dbPath); int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); // 获取数据库文件路径 QString dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString dbPath = QDir(dir).absoluteFilePath(DB_FILENAME); qDebug() << "DB File Path is:" << dbPath; // 检查数据库文件是否存在 bool dbExists = QFile::exists(dbPath); if (!dbExists) { qDebug() << "数据库不存在,正在创建并插入数据..."; if (!createAndInsertData(dbPath)) { qDebug() << "创建数据库或插入数据失败。"; return -1; } qDebug() << "数据库创建并成功插入数据。"; } else { qDebug() << "数据库已存在,跳过创建步骤。"; } // 读取数据 qDebug() << "正在读取数据库中的数据..."; if (!readData(dbPath)) { qDebug() << "读取数据库数据失败。"; return -1; } qDebug() << "数据读取成功。"; return 0; } /** * @brief 创建加密数据库并插入数据 * @param dbPath 数据库文件路径 * @return 成功返回 true,否则返回 false */ bool createAndInsertData(const QString &dbPath) { // 添加 SQLITECIPHER 驱动 QSqlDatabase db = QSqlDatabase::addDatabase("SQLITECIPHER", "create_connection"); db.setDatabaseName(dbPath); db.setPassword(DB_PASSWORD); db.setConnectOptions("QSQLITE_USE_CIPHER=aes256cbc;"); // 使用 AES-256-CBC 加密 if (!db.open()) { qDebug() << "打开数据库失败(创建):" << db.lastError().text(); QSqlDatabase::removeDatabase("create_connection"); return false; } qDebug() << "数据库已打开,用于创建和插入数据。"; QSqlQuery query(db); // 创建表 QString createTableSQL = QString("CREATE TABLE %1 (id INTEGER PRIMARY KEY, name TEXT);").arg(TABLE_NAME); if (!query.exec(createTableSQL)) { qDebug() << "创建表失败:" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("create_connection"); return false; } qDebug() << "表创建成功。"; // 插入数据 query.prepare(QString("INSERT INTO %1 (id, name) VALUES (:id, :name);").arg(TABLE_NAME)); foreach (const QPair<int, QString> &entry, SAMPLE_DATA) { query.bindValue(":id", entry.first); query.bindValue(":name", entry.second); if (!query.exec()) { qDebug() << "插入数据失败 (" << entry.first << "," << entry.second << "):" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("create_connection"); return false; } } qDebug() << "数据插入成功。"; db.close(); QSqlDatabase::removeDatabase("create_connection"); return true; } /** * @brief 读取加密数据库中的数据 * @param dbPath 数据库文件路径 * @return 成功返回 true,否则返回 false */ bool readData(const QString &dbPath) { // 添加 SQLITECIPHER 驱动 QSqlDatabase db = QSqlDatabase::addDatabase("SQLITECIPHER", "create_connection"); db.setDatabaseName(dbPath); db.setPassword(DB_PASSWORD); db.setConnectOptions("QSQLITE_USE_CIPHER=aes256cbc;"); // 使用 AES-256-CBC 加密 if (!db.open()) { qDebug() << "打开数据库失败(读取):" << db.lastError().text(); QSqlDatabase::removeDatabase("read_connection"); return false; } qDebug() << "数据库已打开,用于读取数据。"; QSqlQuery query(db); // 验证 SQLCipher 版本(可选) if (query.exec("PRAGMA cipher_version;")) { if (query.next()) { QString cipher_version = query.value(0).toString(); qDebug() << "SQLCipher 版本:" << cipher_version; } else { qDebug() << "无法获取 SQLCipher 版本。"; } } else { qDebug() << "执行 PRAGMA cipher_version 失败:" << query.lastError().text(); } // 查询数据 QString selectSQL = QString("SELECT id, name FROM %1 ORDER BY id;").arg(TABLE_NAME); if (!query.exec(selectSQL)) { qDebug() << "执行 SELECT 查询失败:" << query.lastError().text(); db.close(); QSqlDatabase::removeDatabase("read_connection"); return false; } // 读取并输出数据 while (query.next()) { int id = query.value(0).toInt(); QString name = query.value(1).toString(); qDebug() << id << ":" << name; } db.close(); QSqlDatabase::removeDatabase("read_connection"); return true; } 运行结果

假设 local.db 文件之前不存在,运行程序后将输出如下内容:

DB File Path is: "C:/Users/用户名/Documents/local.db" 数据库不存在,正在创建并插入数据... 数据库已打开,用于创建和插入数据。 表创建成功。 数据插入成功。 数据库创建并成功插入数据。 正在读取数据库中的数据... 数据库已打开,用于读取数据。 SQLCipher 版本: "4.5.0" 1 : "AAA" 2 : "BBB" 3 : "CCC" 4 : "DDD" 5 : "EEE" 6 : "FFF" 7 : "GGG" 数据读取成功。 常见问题与解决 1. 数据库打开失败,显示“file is not a database”

原因:解密密钥不正确或加密参数不匹配。

解决方法:

确保在打开数据库时使用的密码与创建时一致。确保加密算法和参数(如 QSQLITE_USE_CIPHER)一致。检查 SQLCipher 插件是否正确加载。 2. 无法加载 SQLITECIPHER 驱动

原因:驱动未正确编译或路径配置错误。

解决方法:

确保 SQLCipher 驱动已正确编译并与 Qt 版本兼容。检查驱动插件路径是否在 Qt 的插件搜索路径中。使用 qDebug() << QSqlDatabase::drivers(); 查看可用驱动,确认 SQLITECIPHER 是否存在。 3. 插入或查询数据失败

原因:表未正确创建、SQL 语句有误或加密设置不当。

解决方法:

检查表名和字段是否正确。使用 SQL 工具(如 sqlcipher 命令行工具)验证数据库内容。确认 SQL 语句的语法正确。 总结

在实际应用中,建议进一步优化密码管理机制,避免将密码硬编码在代码中,可以考虑使用更安全的存储方式。此外,根据具体需求,您还可以探索 SQLCipher 提供的更多高级功能,如动态更改密码、密钥派生等。

如果在集成过程中遇到任何问题,欢迎参考 SQLCipher 的官方文档或社区资源,以获得更多支持。

参考

带有加密功能的 SQLite Qt 插件(v1.0) QtCipherSqlitePlugin插件使用 (2) GitHub - devbean/QtCipherSqlitePlugin

标签:

使用Qt插件和SQLCipher实现SQLite数据库加密与解密由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“使用Qt插件和SQLCipher实现SQLite数据库加密与解密

上一篇
enumclass与enum

下一篇
Linux安装Minio