SQLITE3 加密总结.docx

上传人:b****4 文档编号:12255091 上传时间:2023-04-17 格式:DOCX 页数:25 大小:64.38KB
下载 相关 举报
SQLITE3 加密总结.docx_第1页
第1页 / 共25页
SQLITE3 加密总结.docx_第2页
第2页 / 共25页
SQLITE3 加密总结.docx_第3页
第3页 / 共25页
SQLITE3 加密总结.docx_第4页
第4页 / 共25页
SQLITE3 加密总结.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

SQLITE3 加密总结.docx

《SQLITE3 加密总结.docx》由会员分享,可在线阅读,更多相关《SQLITE3 加密总结.docx(25页珍藏版)》请在冰豆网上搜索。

SQLITE3 加密总结.docx

SQLITE3加密总结

SQLITE3加密总结(sqlite3.6.12版本)

董淳光42530(老工号)

***************

2009年4月9日星期四

I.序

时隔上次写《Sqlite3使用总结》已过去2年。

这2年时间我做了好些对自己人生影响很大的事情。

不扯太远了。

2年来一直想把sqlite3的加密搞清楚一些,但一直没时间去做。

这两天终于有空坐下来研究sqlite3的加密方法。

有点收获。

记录下来免得忘记。

我写本文章时,sqlite3最新版本是3.6.12。

我就以这个版本的源代码为例进行分析。

并且,我喜欢它那整合代码,整合成一个.c和一个.h文件。

虽然在vc2003里编辑慢如蜗牛,但是一旦编辑好,以后使用起来不至于每个工程都拖上一堆文件。

工程简洁方便。

所以下面的叙述全部都是以sqlite3v3.6.12整合的源代码为基础展开。

我认为未来sqlite3v3.XX的整合版本大体上都可以用下面介绍的代码进行加解密。

Sqlite3在版本变化中,有一些宏、函数被改名,读者很容易查出来并自己修正。

也有一些函数会被丢弃。

读者应该也可以自行分析出来。

我下面的代码尽可能保持与sqlite版本的兼容性。

不使用那些容易被丢弃的结构或函数。

这样以后就不会常常有人发邮件咨询我能否制作一个最新版本sqlite加密了。

II.问题初分析

首先是要理清sqlite3加解密思路。

这点在2年前就没有做到位。

我本人愚笨,不擅长凭空分析太抽象的事情。

对于软件问题最希望的就是边看现象边理解。

分析sqlite3加密思路也采用具体现象具体分析的方法。

还是vc2003编译器(使用其它编译器的读者可以自行对应着设置,我下面的叙述并没有使用过多的vc2003编译技巧,对应到vc2005、2008,甚至unix下的cc等等都是通用的)。

首先在工程属性里的“C/C++”选项里找到“命令行”,如图:

自己手工敲上“/D"SQLITE_HAS_CODEC"”。

这个意思是在整个工程里预先define“SQLITE_HAS_CODEC”这个宏。

这个宏是sqlite3作者留下的加密接口。

缺省没有这个宏,sqlite3就是不加密的标准版。

定义有这个宏,那么就是要开启加密功能。

设定好宏后,开始编译。

结果,问题现象就出现了:

testSqliteEncerrorLNK2019:

无法解析的外部符号_sqlite3_activate_see,该符号在函数_sqlite3Pragma中被引用

testSqliteEncerrorLNK2019:

无法解析的外部符号_sqlite3_key,该符号在函数_sqlite3Pragma中被引用

testSqliteEncerrorLNK2019:

无法解析的外部符号_sqlite3_rekey,该符号在函数_sqlite3Pragma中被引用

testSqliteEncerrorLNK2019:

无法解析的外部符号_sqlite3CodecAttach,该符号在函数_attachFunc中被引用

testSqliteEncerrorLNK2019:

无法解析的外部符号_sqlite3CodecGetKey,该符号在函数_attachFunc中被引用

意思很明显,有这些函数的定义,但作者没有实现这些函数。

需要我们自己实现。

反之,大概实现了这些函数后就能进行加密了。

 

III.研究结果

参阅了网上某文章,照着他的做法的确可以做出加密效果。

在这里十分感谢那文章的作者。

但我并不满足于此。

我无法确认人家的代码有没有问题。

而且看过他代码,觉得封装程度也并不好。

于是希望通过一些研究判断人家代码可靠性。

同时更进一步进行封装。

研究过程不再赘述,无非就是简单编写先前编译缺少的那些函数体,再对照网上代码编写那些函数内容,然后反复参考、单步调试sqlite3源代码。

这里直接给出最终结果。

下面章节再简述关键点。

我本想做到另外建立配套程序文件来实现,力求跟原始sqlite.c文件全兼容,但可惜的是加解密需要操作sqlite内部数据结构以及内部函数,这些函数和结构没有在sqlite3.h头文件里定义。

所以我不得不把代码写到了sqlite3.c文件里。

最终结果虽然是要修改sqlite.c文件,但我已经让修改地方尽量少。

这样改起来不容易出错。

修改地方总共加有3处,这3处都是添加代码,不需要修改,也没有删除。

需要添加的代码下面全部都有。

Sqlite3.c里需要添加代码的位置大概如下图所示:

下面列出源码。

打开sqlite3.c文件。

在最顶端添加:

#ifdefSQLITE_HAS_CODEC

voidDestroyKeyInBtree(void*lpTree);

#defineMY_DESTROY_KEY(BTREE)DestroyKeyInBtree(BTREE)

#else

#defineMY_DESTROY_KEY(BTREE)

#endif

这段代码用于释放我们自己分配的密钥数据块。

密钥数据块都是我们申请的内存,当数据库关闭时,我希望能释放密钥块内存,否则内存泄露。

但是,sqlite3没有提供数据库关闭进行释放密钥块的接口,得自己添加。

根据网上代码分析,我们释放密钥块时需要获得指定的数据库BTree结构体。

因为每个密钥块是关联到特定的数据库BTree结构体上的。

通过分析代码,不难找到sqlite3_close会调用下面这个函数

SQLITE_PRIVATEintsqlite3BtreeClose(Btree*p)

这是sqlite3的函数。

我确认销毁密钥块的时机就是在这个函数里。

搜索到这代码:

sqlite3BtreeRollback(p);

sqlite3BtreeLeave(p);

就在Leave后面添加我们销毁功能,如下:

/*Rollbackanyactivetransactionandfreethehandlestructure.

Thecalltosqlite3BtreeRollback()dropsanytable-locksheldby

thishandle.

*/

sqlite3BtreeRollback(p);

sqlite3BtreeLeave(p);

MY_DESTROY_KEY(p);//我们添加的删除密钥的功能

/*Iftherearestillotheroutstandingreferencestotheshared-btree

structure,returnnow.Theremainderofthisprocedurecleans

uptheshared-btree.

*/

assert(p->wantToLock==0&&p->locked==0);

这个销毁时机不能再往后放了,再往后BTree相关结构体都已被销毁,我们再从它里面获取我们密钥块指针就会崩溃。

因此在我上面代码那里销毁时机刚好。

接下来修改就简单了。

直接拖到最末尾,3.6.12版本的最后两行有效代码应该是:

#endif/*defined(SQLITE_ENABLE_ICU)*/

#endif/*!

defined(SQLITE_CORE)||defined(SQLITE_ENABLE_FTS3)*/

就在最后那个endif后面添加我下面列出来的所有代码。

 

//////////////////////////////////////////////////////////////////////////

////////////////////下面是我们自己添加的加密函数实现//////////////////////

//////////////////////我认为每个版本的整合sqlite//////////////////////////

////////////////////////只要添加这些函数就可以实现加密功能////////////////

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

#ifndefSQLITE_HAS_CODEC

#ifdefWIN32

#pragmamessage("Wedonotuseencryption")

#endif

#else

#include"./Encrypt.h"

#ifdefWIN32

#pragmamessage("&&&&&&&&Weuseencryption!

&&&&&&&&")

#endif

 

/*

下面结构体是我们封装的一个中间结构体

目的是为了给用户加解密环境提供更为独立的功能,不依赖一些sqlite莫名其妙设计带来的骚扰

结构体中的lpUserBlock才是用户自己DeriveKey返回的结构体

*/

struct_TmpUserKeyBlock

{

void*lpUserBlock_old;

void*lpUserBlock_new;

};

typedefstruct_TMP_ENCRYPT_BLOCK

{

struct_TmpUserKeyBlocklpUserKey;

intiPageSize;

intiEncryptingBufSize;

charpEncryptingBuf[1];

}TMP_ENCRYPT_BLOCK,*PTMP_ENCRYPT_BLOCK;

/*

下面是对用户DeriveKey、CopyKey、DestroyKey的封装。

封装目的是sqlite解密和加密操作的目标数据内存是不一样的。

如果不加以封装,后面编写加解密函数就

不得不考虑这些细节。

封装后用户函数就不需要再考虑这些部分了。

*/

PTMP_ENCRYPT_BLOCKDeriveKey_Tmp(constvoid*pKey,intnKeyLen,intiPageSize)

{

void*pUserKey;

PTMP_ENCRYPT_BLOCKpTmpBlock;

intiSize=iPageSize+128;

if(NULL==pKey||0==nKeyLen)

{

returnNULL;

}

pTmpBlock=(PTMP_ENCRYPT_BLOCK)malloc(sizeof(TMP_ENCRYPT_BLOCK)+iSize);

if(NULL==pTmpBlock)

{

returnNULL;

}

pTmpBlock->iEncryptingBufSize=iSize;//+128是为了多分配一些内存,免得跟sqlite打交道哪里出现溢出

pTmpBlock->iPageSize=iPageSize;

 

pUserKey=(void*)DeriveKey(pKey,nKeyLen,iPageSize);

if(NULL==pUserKey)

{

free(pTmpBlock);

returnNULL;

}

//刚开始时,新旧密钥应该都一致

pTmpBlock->lpUserKey.lpUserBlock_old=pUserKey;

pTmpBlock->lpUserKey.lpUserBlock_new=pUserKey;

returnpTmpBlock;

}

//复制一份数据库

PTMP_ENCRYPT_BLOCKCopyKey_Tmp(constPTMP_ENCRYPT_BLOCKlpKey)

{

PTMP_ENCRYPT_BLOCKpNew;

void*pUserKey;

intiSize;

if(NULL==lpKey)

returnNULL;

iSize=lpKey->iEncryptingBufSize;

pNew=(PTMP_ENCRYPT_BLOCK)malloc(iSize+sizeof(TMP_ENCRYPT_BLOCK));

if(NULL==pNew)

returnNULL;

pUserKey=CopyKey(lpKey->lpUserKey.lpUserBlock_new);

if(NULL==pUserKey)

{

free(pNew);

returnNULL;

}

pNew->lpUserKey.lpUserBlock_old=pUserKey;

pNew->lpUserKey.lpUserBlock_new=pUserKey;

pNew->iPageSize=lpKey->iPageSize;

pNew->iEncryptingBufSize=iSize;

returnpNew;

}

//释放密钥

voidDestroyKey_Tmp(PTMP_ENCRYPT_BLOCKlpKey)

{

if(NULL==lpKey)

return;

if(NULL!

=lpKey->lpUserKey.lpUserBlock_new)

DestroyKey(lpKey->lpUserKey.lpUserBlock_new);

free(lpKey);

return;

}

 

//文件加密解密的中间函数

//本函数做简单处理之后会调用My_sqlite3Codec函数

//nMode类型:

//case0:

//Undoa"case7"journalfileencryption

//case2:

//Reloadapage

//case3:

//Loadapage

//case6:

//Encryptapageforthemaindatabasefile

//case7:

//Encryptapageforthejournalfile

void*My_sqlite3Codec_tmp(void*pArg,void*data,unsignedintnPageNum,intnMode)

{

PTMP_ENCRYPT_BLOCKlpTmpArg;

void*lpRet;

void*lpUserKey;

if(!

pArg)

{

returndata;

}

lpTmpArg=(PTMP_ENCRYPT_BLOCK)pArg;

if(nMode<=5)

{

//解密

lpRet=data;

lpUserKey=lpTmpArg->lpUserKey.lpUserBlock_old;

}

else

{

//加密

lpRet=lpTmpArg->pEncryptingBuf;

memcpy(lpRet,data,lpTmpArg->iPageSize);

if(nMode==7)

{

//操作事务文件,需要用旧密钥

lpUserKey=lpTmpArg->lpUserKey.lpUserBlock_old;

}

else

{

//否则就是写入数据库页面,用新密钥

lpUserKey=lpTmpArg->lpUserKey.lpUserBlock_new;

}

}

if(NULL==lpUserKey)

returnlpRet;

My_sqlite3Codec(lpUserKey,lpRet,lpTmpArg->iPageSize,nPageNum,nMode);

returnlpRet;

}

//用于重设密钥

staticvoid*My_sqlite3pager_get_codecarg(Pager*pPager)

{

return(pPager->xCodec)?

pPager->pCodecArg:

NULL;

}

/*

设置数据库页面加解密参数

*/

voidMy_sqlite3pager_set_codec(Pager*pPager,void*(*xCodec)(void*,void*,Pgno,int),void*pCodecArg)

{

pPager->xCodec=xCodec;

pPager->pCodecArg=pCodecArg;

}

/*

**返回数据库磁盘文件总页面数

**IfthePENDING_BYTEliesonthepagedirectlyaftertheendofthe

**file,thenconsiderthispagepartofthefiletoo.Forexample,if

**PENDING_BYTEisbyte4096(thefirstbyteofpage5)andthesizeofthe

**fileis4096bytes,5isreturnedinsteadof4.

*/

intMy_sqlite3pager_pagecount(Pager*pPager)

{

sqlite_int64n;

assert(pPager);

if(pPager->dbSize>=0)

{

n=pPager->dbSize;

}

else

{

if(sqlite3OsFileSize(pPager->fd,&n)!

=SQLITE_OK)

{

pager_error(pPager,SQLITE_IOERR);

return0;

}

if(n>0&&npageSize)

{

n=1;

}

else

{

n/=pPager->pageSize;

}

if(pPager->state!

=PAGER_UNLOCK)

{

pPager->dbSize=(Pgno)n;

}

}

if(n==(PENDING_BYTE/pPager->pageSize))

{

n++;

}

return(int)n;

}

 

//设置密钥

voidMySetKey(sqlite3*db,constvoid*pKey,intnKey)

{

//设置密钥简单,直接调用密钥设置接口就行了

sqlite3CodecAttach(db,0,pKey,nKey);

}

//重新设置密钥

intMyResetKey(sqlite3*db,constvoid*pKey,intnKey)

{

Btree*pbt=db->aDb[0].pBt;

Pager*p=sqlite3BtreePager(pbt);

PTMP_ENCRYPT_BLOCKpNewKey=NULL;

PTMP_ENCRYPT_BLOCKpOldKey=(PTMP_ENCRYPT_BLOCK)My_sqlite3pager_get_codecarg(p);

void*lpUserOld=NULL;

intrc=SQLITE_ERROR;

//总之都是要新分配一个新密钥空间的

pNewKey=DeriveKey_Tmp(pKey,nKey,(int)(p->pageSize));

if(NULL==pOldKey&&NULL==pNewKey)

{

returnSQLITE_OK;

}

if(NULL==pOldKey)

{

pNewKey->lpUserKey.lpUserBlock_old=NULL;

My_sqlite3pager_set_codec(sqlite3BtreePager(pbt),My_sqlite3Codec_tmp,pNewKey);

}

else

{

lpUserOld=pOldKey->lpUserKey.lpUserBlock_new;

if(NULL!

=pNewKey)

pOldKey->lpUserKey.lpUserBlock_new=pNewKey->lpUserKey.lpUserBlock_new;

else

pOldKey->lpUserKey.lpUserBlock_new=NULL;

}

 

//开始事务,加密应该要加密的页面

rc=sqlite3BtreeBeginTrans(pbt,1);

if(!

rc)

{

//用新密钥重新加密并写入页面

PgnonPage=My_sqlite3pager_pagecount(p);

PgnonSkip=PAGER_MJ_PGNO(p);

void*pPage;

Pgnon;

for(n=1;rc==SQLITE_OK&&n<=nPage;n++)

{

if(n==nSkip)continue;

rc=sqlite3PagerGet(p,n,&pPage);

if(!

rc)

{

rc=sqlite3PagerWrite(pPage);

sqlite3PagerUnref(pPage);

}

}

}

//尝试提交事务

if(!

rc)

{

rc=sqlite3BtreeCommit(pbt);

}

//如果失败,就回滚事务

if(rc)

{

sqlite3BtreeRollback(pbt);

}

if(NULL!

=pOldKey)

{

if(NULL!

=lpUserOld)

{

pOldKey->lpUserKey.lpUserBlock_new=lpUserOld;

}

else

{

pO

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 法学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1