QtCipherSqlitePlugin插件使用(1)

写在前面

注意: 各版本的不同,关注兼容性;

  • 本文演示版本: 1.3
  • 正考虑为其增加 cmake 脚本, 默认使用 qmake 编译源码;
  • 本文演示Qt版本: 5.14
  • 本文演示操作系统: windows11

目标

  • 本文将介绍 QtCipherSqlitePlugin 的以下三方面
    • 构建
    • 验证
    • 使用

概述

  • sqlite默认不支持文件加密, 商业版支持加密。
  • QtCipherSqlitePlugin 是一个开源加密sqlite数据库的项目, 可以在 QtCipherSqlitePlugin githubQtCipherSqlitePlugin gitee获取源码。
  • 基于某些原因,本文不提供源码, 有需要,请联系文末 二维码 或 私信。

以下使用,假定已下载好官方源码
源码下载:

git clone https://github.com/devbean/QtCipherSqlitePlugin.git 

构建

官方源码中提供了范例和插件源码。 下文演示为: 构建含演示范例。

步骤

  • 1 来到源码解压目录
cd QtCipherSqlitePlugin
  • 2 使用QtCreator打开 QtCipherSqlitePlugin目录下的QtCipherSqlitePlugin.pro工程文件
  • 3 发现官方默认使用的时VS2015构建套件, 而本机只有Qt5.14, 故而将构建套件改为本机的Qt套件,方法(基于步骤2):

  • 5 至于输出输出目录,可自行选择,这里,演示使用默认的shadow build

需要注意: 'sqlitecipher/sqlite3/sqlite3.pri' 中 的CODEC_TYPE保存了加密算法,默认值为:CODEC_TYPE_CHACHA20, 可以改为:CODEC_TYPE_AES128CODEC_TYPE_AES256CODEC_TYPE_CHACHA20CODEC_TYPE_SQLCIPHER , 请根据需要修改

  • 6 回到 QtCreator中,点击 或者 快捷键: ctrl+b 执行项目构建。构建结束后, 找到输出目录,可见生成成功的动态库、lib和cmake 等一系列文件
    • lib 和 动态库位于 path/plugins/sqldrivers
    • 插件对应的cmake文件位于: path/lib/cmake/Qt5Sql

下面需要自行对应qt安装目录。

  • 7 将生成的动态库(debug和release)拷贝到Qt的安装目录: path/plugins/sqldrivers。 我这里的路径为:
xx/5.14.2/msvc2015_64/plugins/sqldrivers
  • 8 将输出目录中,cmake文件拷贝到Qt的安装目录/path/lib/cmake/Qt5Sql,我的路径为:
xxx/5.14.2/msvc2015_64/lib/cmake/Qt5Sql
  • 9 将输出的lib文件拷贝到path/lib。 我这里为
xx/5.14.2/msvc2015_64/lib

构建到此结束

验证

  • 1 创建测试项目,增加Sql模块, 我这里创建了一个widget的项目(QtVs插件)QtWidgetsApplication1。 QtWidgetsApplication1 的构造函数中增加以下关键代码:
#include <QSqlDatabase>
#include <QDebug>
...
QtWidgetsApplication1::QtWidgetsApplication1(QObject*parent) : QObject(parent)
{
	...
	qDebug() << "\n\n\n";
	qDebug() << QSqlDatabase::drivers();
	qDebug() << "\n\n\n";
}
  • 2 编译并运行测试项目,如果输出 SQLITECIPHER关键字,则证明可使用 QtCipherSqlitePlugin插件。 否则,请检查构建是否正确。

使用

  • QtCipherSqlitePlugin 插件提供了对sqlite数据库文件的加密、更新加密密码、删除密码和的功能。
  • 可在运行时指定 CIPHER 详见下面的代码, 自己封装了一个类,包含了加密、删除和更新密钥 以及测试 加密数据库能否打开 4个接口。 copy即可使用

核心代码

...
enum SQLITE_CIPHER
{
	SC_aes128cbc = 1,
	SC_aes256cbc = 2,
	SC_chacha20 = 3,
	SC_sqlcipher = 5,
};


class Sqlite : public QObject
{
Q_OBJECT
public:
	Sqlite(QObject* parent = nullptr) : QObject(parent)
	{
		m_cipherHash.insert(SC_aes128cbc, "aes128cbc");
		m_cipherHash.insert(SC_aes256cbc, "aes256cbc");
		m_cipherHash.insert(SC_chacha20, "chacha20");
		m_cipherHash.insert(SC_sqlcipher, "sqlcipher");
	}

	/// -------------------------------------------------------------------------------
	/// @brief:		加密文件
	/// @param: 	const QString & sqlite_file - 哪个文件
	/// @param: 	const QString & password - 密码
	/// @param: 	const QString & codec_type - 加密器
	///  @ret:		int
	///				0 - 成功
	///				1 - 失败, 参数 sqlite_file 为空 或者 codec_type 为空, 调用errorMsg获取错误详情
	///				2 - 失败,调用 errorMsg 获取错误详情
	/// -------------------------------------------------------------------------------
	int encodeFile(const QString& sqlite_file, const QString& password, const SQLITE_CIPHER& codec_type)
	{
		if ((0 == sqlite_file.length()) || (false == m_cipherHash.contains(codec_type)))
		{
			m_errorStr = tr("the param's length is false, please check or the codec_type is false");
			return 1;
		}

		QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
		dbconn.setDatabaseName(sqlite_file);
		dbconn.setPassword(password); 
		const QString conn_str = QString("QSQLITE_USE_CIPHER=%1;QSQLITE_CREATE_KEY").arg(m_cipherHash.value(codec_type));
		dbconn.setConnectOptions(conn_str);
		if (!dbconn.open())
		{
			m_errorStr = dbconn.lastError().driverText();
			return 2;
		}

		dbconn.close();

		return 0;
	}

	/// -------------------------------------------------------------------------------
	/// @brief:		更新文件密钥
	/// @param: 	const QString & sqlite_file - 哪个文件 
	/// @param: 	const QString & old_password - 先前的密钥
	/// @param: 	const QString & new_password - 新的密钥
	/// @param: 	const SQLITE_CIPHER & code_type - 加密器
	///  @ret:		int
	///				1 - 失败, 参数 sqlite_file 为空 或者 codec_type 为空, 调用errorMsg获取错误详情
	///				2 - 失败,调用 errorMsg 获取错误详情
	/// -------------------------------------------------------------------------------
	int updateFileKey(const QString& sqlite_file, const QString& old_password, const QString& new_password, const SQLITE_CIPHER& code_type)
	{
		if ((0 == sqlite_file.length()) || (false == m_cipherHash.contains(codec_type)))
		{
			m_errorStr = tr("the param's length is false, please check or the codec_type is false");
			return 1;
		}

		QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
		dbconn.setDatabaseName(sqlite_file);
		dbconn.setPassword(old_password);
		const QString conn_str = QString("QSQLITE_USE_CIPHER=%1;QSQLITE_UPDATE_KEY=%2").arg(m_cipherHash.value(codec_type)).arg(new_password);
		dbconn.setConnectOptions(conn_str);
		if (!dbconn.open())
		{
			m_errorStr = dbconn.lastError().driverText();
			return 2;
		}

		dbconn.close();

		return 0;
	}

	/// -------------------------------------------------------------------------------
	/// @brief:		删除文件
	/// @param: 	const QString & sqlite_file - 数据库文件
	/// @param: 	const QString & password - 密码
	/// @param: 	const SQLITE_CIPHER & codec_type - 加密器
	///  @ret:		int
	///				1 - 失败, 参数 sqlite_file 为空 或者 codec_type 为空, 调用errorMsg获取错误详情
	///				2 - 失败,调用 errorMsg 获取错误详情
	/// -------------------------------------------------------------------------------
	int removeFileKey(const QString& sqlite_file, const QString& password, const SQLITE_CIPHER& codec_type)
	{
		if ((0 == sqlite_file.length()) || (false == m_cipherHash.contains(codec_type)))
		{
			m_errorStr = tr("the param's length is false, please check or the codec_type is false");
			return 1;
		}

		QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
		dbconn.setDatabaseName(sqlite_file);
		dbconn.setPassword(password);
		const QString conn_str = QString("QSQLITE_USE_CIPHER=%1;QSQLITE_REMOVE_KEY").arg(m_cipherHash.value(codec_type));
		dbconn.setConnectOptions(conn_str);
		if (!dbconn.open())
		{
			m_errorStr = dbconn.lastError().driverText();
			return 2;
		}

		dbconn.close();

		return 0;
	}

	/// -------------------------------------------------------------------------------
	/// @brief:		测试加密文件是否能打开成功
	/// @param: 	const QString & sqlite_file - 哪个加密文件
	/// @param: 	const QString & password - 加密文件的密码
	/// @param: 	const SQLITE_CIPHER & codec_type - 加密器
	///  @ret:		int
	///				1 - 失败, 参数 sqlite_file 为空 或者 codec_type 为空, 调用errorMsg获取错误详情
	///				2 - 失败,调用 errorMsg 获取错误详情	
	/// -------------------------------------------------------------------------------
	int testFileKey(const QString& sqlite_file, const QString& password, const SQLITE_CIPHER& codec_type)
	{
		if ((0 == sqlite_file.length()) || (false == m_cipherHash.contains(codec_type)))
		{
			m_errorStr = tr("the param's length is false, please check or the codec_type is false");
			return 1;
		}

		QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
		dbconn.setDatabaseName(sqlite_file);
		dbconn.setPassword(password);
		const QString conn_str = QString("QSQLITE_USE_CIPHER=%1").arg(m_cipherHash.value(codec_type));
		dbconn.setConnectOptions(conn_str);
		if (!dbconn.open())
		{
			m_errorStr = dbconn.lastError().driverText();
			return 2;
		}

		dbconn.close();

		return 0;
	}
private:
	using HashIntStr		= QHash<int, QString>;
	HashIntStr				m_cipherHash;
}

完。

posted @ 2023-03-03 01:00  mohist  阅读(783)  评论(0编辑  收藏  举报