最近需要使用sqlite,但sqlite是明文保存数据存在风险,在网上查了很多Qt加密sqlite的方式主要是QtCipherSqlitePlugin为主,但此种方法虽然可用Qt驱动但无法从程序外解密,不便于维护查看,故选用了SQLCipher。
SQLCipher是一个开源的软件,它提供的sqlite数据库的透明加密。开源版需要自行编译,如有需要可在本文末尾资源中心下载.lib与.dll文件
本文使用了SQLCipher的C API 参考Qt的操作方式做了一个简易封装。
头文件
#pragma once
#pragma execution_character_set("utf-8")
#define SQLITE_HAS_CODEC
#include"sqlite3.h"
#pragma comment(lib,"sqlcipher.lib")
#pragma comment(lib,"libsqlite3.lib")
class MSqlcipher :public QObject
{
Q_OBJECT
public:
MSqlcipher(QObject* parent = nullptr);
MSqlcipher(QString databaseName, QByteArray key, QObject* parent = nullptr);
~MSqlcipher();
bool open(QString databaseName, QByteArray key);
bool open();
void close();
bool isOpen();// This will be true if the database is open
QPair<QString, QString> getLatestError();// get lates error
QStack<QPair<QString, QString>> getError();//get all error
bool exec(QByteArray sql);
bool next();
QVariant value(ushort index);
static bool isActive();// It will be true if the database returns the result
private:
bool openDB();
static int dbCallBack(void* callBackArg, int columnSize, char** columnValue, char** columnName);
void clearContainer();
private:
QString mDbName;
QByteArray mKey; // sqlcipher pwd; PRAGMA key = mKey
QStack<QPair<QString, QString>> mErrors; // first:sql second:message
static bool mState;
bool mIsOpen;
sqlite3* mDb;
int mResult;
static QQueue<QVariantList*> mSqlValue;
QList<QVariant>* mCurrentValueList;
};
源文件
#include "MSqlcipher.h"
bool MSqlcipher::mState = false;
QQueue<QVariantList*> MSqlcipher::mSqlValue;
MSqlcipher::MSqlcipher(QObject* parent)
:QObject(parent), mDb(nullptr), mIsOpen(false)
, mResult(0), mCurrentValueList(nullptr)
{
}
MSqlcipher::MSqlcipher(QString databaseName, QByteArray key, QObject* parent)
: QObject(parent), mDb(nullptr), mKey(key), mDbName(databaseName)
, mIsOpen(false), mResult(0), mCurrentValueList(nullptr)
{
}
MSqlcipher::~MSqlcipher()
{
close();
}
bool MSqlcipher::open(QString databaseName, QByteArray key)
{
if (mIsOpen)
{
QPair<QByteArray, QByteArray> infor(QByteArray(), "sql is opened");
mErrors.push(infor);
return false;
}
mDbName = databaseName;
mKey = key;
return openDB();
}
bool MSqlcipher::open()
{
if (mIsOpen)
{
QPair<QByteArray, QByteArray> infor(QByteArray(), "sql is opened");
mErrors.push(infor);
return false;
}
return openDB();
}
void MSqlcipher::close()
{
if (!mResult)
return;
mResult = sqlite3_close(mDb);
mErrors.clear();
mIsOpen = false;
clearContainer();
}
bool MSqlcipher::isOpen()
{
return mIsOpen;
}
QPair<QString, QString> MSqlcipher::getLatestError()
{
if (mErrors.isEmpty())
return {};
return mErrors.pop();
}
QStack<QPair<QString, QString>> MSqlcipher::getError()
{
QStack<QPair<QString, QString>> result = mErrors;
mErrors.clear();
return result;
}
bool MSqlcipher::exec(QByteArray sql)
{
clearContainer();
mResult = sqlite3_exec(mDb, sql.data(), &MSqlcipher::dbCallBack, nullptr, nullptr);
if (mResult != SQLITE_OK)
{
QString errorInfor = "sql exec fail" + QString(sqlite3_errmsg(mDb));
QPair<QString, QString> infor(sql, errorInfor);
mErrors.push(infor);
return false;
}
return true;
}
void MSqlcipher::clearContainer()
{
mState = false;
if (mCurrentValueList != nullptr)
{
delete mCurrentValueList;
mCurrentValueList = nullptr;
}
while (!mSqlValue.isEmpty())
delete mSqlValue.dequeue();
}
bool MSqlcipher::next()
{
if (mCurrentValueList != nullptr)
{
delete mCurrentValueList;
mCurrentValueList = nullptr;
}
if (mSqlValue.isEmpty())
{
mState = false;
return false;
}
mCurrentValueList = mSqlValue.dequeue();
return true;
}
QVariant MSqlcipher::value(ushort index)
{
if (mCurrentValueList == nullptr)
return {};
index = index >= mCurrentValueList->size() ? mCurrentValueList->size() - 1 : index;
return mCurrentValueList->at(index);
}
bool MSqlcipher::isActive()
{
return mState;
}
bool MSqlcipher::openDB()
{
mResult = sqlite3_open(mDbName.toLatin1().data(), &mDb);
if (mResult != SQLITE_OK)
{
QPair<QByteArray, QByteArray> infor(QByteArray(), "sql failed to open");
mErrors.push(infor);
return false;
}
mIsOpen = true;
clearContainer();
mResult = sqlite3_key(mDb, mKey.data(), mKey.size());
if (mResult != SQLITE_OK)
{
QPair<QByteArray, QByteArray> infor(QByteArray(), "password setting failure");
mErrors.push(infor);
close();
return false;
}
return true;
}
int MSqlcipher::dbCallBack(void* callBackArg, int columnSize, char** columnValue, char** columnName)
{
if (!columnSize)
return 0;
mState = true;
QVariantList* oneRowValue = new QVariantList;
for (ushort i = 0; i < columnSize; i++)
*oneRowValue << QVariant(QString(columnValue[i]));
mSqlValue.enqueue(oneRowValue);
return 0;
}
有好的建议请在评论区留言。
如需相关源码请转至资源中心