Sqlite 3.7.14.1 xxtea 加密算法

http://www.jb51.cc/article/p-fgfdxxce-yd.html

最近在研空Sqlite加密算法,东拼西凑,还没研究出AES怎么用,欢迎指正,交流

1.从官方下载最新版本的Sqlite目前

www.sqlite.org

是:如sqlite-amalgamation-3071401.zip

2.在VS中添加一个空项目

3.解压sqlite-amalgamation-3071401.zip

复制出里面的sqlite3.h和sqlite3.c文件,放在工程目录中,并添加到工程

4.在工程中添加"sqlite3crypt.h",并添加以下代码

#ifndef  DCG_SQLITE_CRYPT_FUNC_
#define  DCG_SQLITE_CRYPT_FUNC_

//#ifdef SQLITE_HAS_CODEC

typedef unsigned char BYTE;
// 加密结构
#define CRYPT_OFFSET 8
typedef struct _CryptBlock {

	BYTE* ReadKey; // 读数据库和写入事务的密钥
	BYTE* WriteKey; // 写入数据库的密钥
	int PageSize; // 页的大小
	BYTE* Data;

} CryptBlock,*LPCryptBlock;

#ifndef DB_KEY_LENGTH_BYTE        // 密钥长度
#define DB_KEY_LENGTH_BYTE   16   // 密钥长度
#endif

#ifndef DB_KEY_PADDING            // 密钥位数不足时补充的字符
#define DB_KEY_PADDING       0x33 // 密钥位数不足时补充的字符
#endif

// 加密函数
int sqlite3_encrypt(unsigned char * pData,unsigned int data_len,unsigned char * key,unsigned int len_of_key);
 
// 解密函数
int sqlite3_dencrypt(unsigned char * pData,unsigned int len_of_key);

// ===
// 被sqlite3调用的加/解密函数
void* sqlite3Codec(void *pArg,void *data,int nPageNum,int nMode);

// 释放而加/密内存的回调
void sqlite3CodecFree(void* arg);

// 验证密码接口
unsigned char* DeriveKey(const void *pKey,int nKeyLen);

// 创建或更新一个页的加密算法索引.此函数会申请缓冲区.
LPCryptBlock CreateCryptBlock(unsigned char* hKey,int nPageSize,LPCryptBlock pExisting);

//#endif // SQLITE_HAS_CODEC
#endif

5.在工程中添加"sqlite3crypt.c",并添加以下代码

#include "sqlite3crypt.h"
#include "sqlite3.h"
#include <memory.h>
#include <stdlib.h>

int xxtea( int * v,int n,int * k ) {  
	unsigned int z/*=v[n-1]*/,y=v[0],sum=0,e,DELTA=0x9e3779b9 ;  
	int m,p,q ;  
	if ( n>1) {  
		/* Coding Part */  
		z = v[n-1];  
		q = 6+52/n ;  
		while ( q-- > 0 ) {  
			sum += DELTA ;  
			e = sum >> 2&3 ;  
			for ( p = 0 ; p < n-1 ; p++ ){  
				y = v[p+1],z = v[p] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
			}  
			y = v[0] ;  
			z = v[n-1] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
		}  
		return 0 ;  

		/* Decoding Part */  
	}else if ( n <-1 ) {  
		n = -n ;  
		q = 6+52/n ;  
		sum = q*DELTA ;  
		while (sum != 0) {  
			e = sum>>2 & 3 ;  
			for (p = n-1 ; p > 0 ; p-- ){  
				z = v[p-1],y = v[p] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
			}  
			z = v[n-1] ;  
			y = v[0] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
			sum -= DELTA ;   
		}  
		return 0 ;  
	}  

	return 1 ;  
} 

// 其它算法,未实现
 // ==============================================================================
int sqlite3_encrypt(unsigned char * pData,unsigned int len_of_key)
{
	return 0;
}

// 其它算法,未实现
// ==============================================================================
int sqlite3_dencrypt( unsigned char * pData,unsigned int len_of_key)
{
	
	return 0;
}

// ==============================================================================
void * sqlite3Codec(void *pArg,int nMode) {

	 LPCryptBlock pBlock = (LPCryptBlock) pArg;
       unsigned int dwPageSize = 0;
	   int len = 0;
 
       if (!pBlock)
              return data;
       // 确保pager的页长度和加密块的页长度相等.如果改变,就需要调整.
       if (nMode != 2) {
		   int a = 0;
		   a++;
             // PgHdr *pageHeader;
 
             // pageHeader = DATA_TO_PGHDR(data);
             // if (pageHeader->pPager->pageSize != pBlock->PageSize) {
                    // CreateCryptBlock(0,pageHeader->pPager,pBlock);
              //}
       }
 
       switch (nMode) {
       case 0: // Undo a "case 7" journal file encryption
       case 2: //重载一个页
		   case 3: //载入一个页  
        if (!pBlock->ReadKey) break;  
  
        len = 0 - (pBlock->PageSize / 4);  
        xxtea(data,len,pBlock->ReadKey);  
  
        break;  
    case 6: 

		//加密一个主数据库文件的页  
        if (!pBlock->WriteKey) break;  
  
        memcpy(pBlock->Data + CRYPT_OFFSET,data,pBlock->PageSize);  
        data = pBlock->Data + CRYPT_OFFSET;  
  
  
        len = pBlock->PageSize / 4;  
        xxtea(data,pBlock->WriteKey);  
        break;  

    case 7:
		// 加密事务文件的页
        if (!pBlock->ReadKey) break;  
        memcpy(pBlock->Data + CRYPT_OFFSET,pBlock->PageSize);  
        data = pBlock->Data + CRYPT_OFFSET;  
        len = pBlock->PageSize / 4;  
        xxtea(data,pBlock->ReadKey);  
        break; 
		   /*
       case 3: //载入一个页
              if (!pBlock->ReadKey)
                     break;
 
			  memcpy(pBlock->Data + CRYPT_OFFSET,pBlock->PageSize);
			  data = pBlock->Data + CRYPT_OFFSET;
			  dwPageSize = pBlock->PageSize;
				
			  // 解密
              sqlite3_dencrypt((BYTE*)data,dwPageSize,pBlock->ReadKey,DB_KEY_LENGTH_BYTE);
 
              break;
 
       case 6: //加密一个主数据库文件的页
              if (!pBlock->WriteKey)
                     break;
 
              memcpy(pBlock->Data + CRYPT_OFFSET,pBlock->PageSize);
              data = pBlock->Data + CRYPT_OFFSET;
              dwPageSize = pBlock->PageSize;
 
			  // 加密
              sqlite3_encrypt((BYTE*)data,pBlock->WriteKey,DB_KEY_LENGTH_BYTE); 
 
              break;
 
       case 7: 
			  // 加密事务文件的页
              // 在正常环境下,读密钥和写密钥相同. 当数据库是被重新加密的,读密钥和写密钥未必相同.
              // 回滚事务必要用数据库文件的原始密钥写入.因此,当一次回滚被写入,总是用数据库的读密钥,// 这是为了保证与读取原始数据的密钥相同.
              //
              if (!pBlock->ReadKey)
                     break;
 
              memcpy(pBlock->Data + CRYPT_OFFSET,pBlock->PageSize);
              data = pBlock->Data + CRYPT_OFFSET;
              dwPageSize = pBlock->PageSize;
 
              sqlite3_encrypt((BYTE*)data,DB_KEY_LENGTH_BYTE);/*调用我的加密函数*/
 
             // break;*/
       }

	   return data;
}

// ==============================================================================
void sqlite3CodecFree(void* pArg) {
	if (pArg)
	{
		LPCryptBlock pBlock = (LPCryptBlock)pArg;
		//销毁读密钥.
		if (pBlock->ReadKey) {
			sqlite3_free(pBlock->ReadKey);
		}

		//如果写密钥存在并且不等于读密钥,也销毁.

		if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey) {
			sqlite3_free(pBlock->WriteKey);
		}

		if (pBlock->Data) {
			sqlite3_free(pBlock->Data);
		}

		//释放加密块.
		sqlite3_free(pBlock);
	}
}


// =====================================================================================
LPCryptBlock CreateCryptBlock(unsigned char* hKey,LPCryptBlock pExisting)
{
	LPCryptBlock pBlock;

	if (!pExisting) //创建新加密块
	{
		pBlock = sqlite3_malloc(sizeof(CryptBlock));
		memset(pBlock,sizeof(CryptBlock));
		pBlock->ReadKey = hKey;
		pBlock->WriteKey = hKey;
		pBlock->PageSize = nPageSize;
		pBlock->Data = (unsigned char*) sqlite3_malloc(
			pBlock->PageSize + CRYPT_OFFSET);
	}
	else //更新存在的加密块
	{
		pBlock = pExisting;

		if (pBlock->PageSize != nPageSize && !pBlock->Data) {
			sqlite3_free(pBlock->Data);
			pBlock->PageSize = nPageSize;

			pBlock->Data = (unsigned char*) sqlite3_malloc(
				pBlock->PageSize + CRYPT_OFFSET);
		}
	}

	memset(pBlock->Data,pBlock->PageSize + CRYPT_OFFSET);

	return pBlock;
}

// 从用户提供的缓冲区中得到一个加密密钥
// =====================================================================================
unsigned char * DeriveKey(const void *pKey,int nKeyLen)
{
	unsigned char * hKey = 0;
	int j;

	if (pKey == 0 || nKeyLen == 0)
	{
		return 0;
	}

	hKey = sqlite3_malloc(DB_KEY_LENGTH_BYTE + 1);
	if (hKey == 0)
	{
		return 0;
	}

	hKey[DB_KEY_LENGTH_BYTE] = 0;

	if (nKeyLen < DB_KEY_LENGTH_BYTE)
	{
		memcpy(hKey,pKey,nKeyLen); //先拷贝得到密钥前面的部分
		j = DB_KEY_LENGTH_BYTE - nKeyLen;

		//补充密钥后面的部分
		memset(hKey + nKeyLen,DB_KEY_PADDING,j);
	}
	else
	{
		//密钥位数已经足够,直接把密钥取过来
		memcpy(hKey,DB_KEY_LENGTH_BYTE);
	}

	return hKey;
}


6.在splite3.c文件的最后面添加以下代码

#ifdef SQLITE_HAS_CODEC
#include "sqlite3crypt.h"

// 得到页所对应的编/解码块
static void * sqlite3pager_get_codecarg(struct Pager *pPager)
{
       return (pPager->xCodec) ? pPager->pCodec : 0;
}

// 设置页所对应的编/解码块
// @Param pPager 要加密的页
// @Param xCodec 加/解密回调
// @Param xCodecFree 释放页对应的加/解密模块回调
// @Param pCodecArg 加/解密模块
void sqlite3pager_set_codec(Pager *pPager,void *(*xCodec)(void*,void*,Pgno,int),void (*xCodecFree)(void*),void *pCodecArg)
{
       pPager->xCodec = xCodec;
       pPager->pCodec = pCodecArg;
	   pPager->xCodecFree = xCodecFree;
}


// 实现Sqlite3接口
// ===============================================================================
// ===============================================================================
// 下面是编译时提示缺少的函数 
// 这个函数不需要做任何处理,获取密钥的部分在下面 DeriveKey 函数里实现
void sqlite3CodecGetKey(struct sqlite3* db,int nDB,void** Key,int* nKey) {
	return;
}

// 这个函数好像是 sqlite 3.3.17前不久才加的,以前版本的sqlite里没有看到这个函数
// 这个函数我还没有搞清楚是做什么的,它里面什么都不做直接返回,对加解密没有影响
void sqlite3_activate_see(const char* right) {
	return;
}
 
 // 实现加密接口
int sqlite3_key(sqlite3 *db,const void *pKey,int nKey)
{
	return sqlite3CodecAttach(db,nKey);
}
 
 // 实现解密接口
int sqlite3_rekey(sqlite3 *db,int nKey)
{
	Pgno nSkip;
     void *pPage;
     Pgno n;
       Btree *pbt = db->aDb[0].pBt;
       Pager *p = sqlite3BtreePager(pbt);
       LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(p);
       unsigned char * hKey = DeriveKey(pKey,nKey);
       int rc = SQLITE_ERROR;
 
       if (!pBlock && !hKey)
              return SQLITE_OK;
 
       //重新加密一个数据库,改变pager的写密钥,读密钥依旧保留.
       if (!pBlock) //加密一个未加密的数据库
       {
              pBlock = CreateCryptBlock(hKey,p->pageSize,NULL);
              pBlock->ReadKey = 0; // 原始数据库未加密
              sqlite3pager_set_codec(sqlite3BtreePager(pbt),sqlite3Codec,sqlite3CodecFree,pBlock);
       }
       else // 改变已加密数据库的写密钥
       {
              pBlock->WriteKey = hKey;
       }
 
       // 开始一个事务
       rc = sqlite3BtreeBeginTrans(pbt,1);
       if (!rc)
       {
              // 用新密钥重写所有的页到数据库。
              //Pgno nPage = sqlite3PagerPagecount(p);
		   int nPage = 0;
		   sqlite3PagerPagecount(p,&nPage);
             
			 nSkip = PAGER_MJ_PGNO(p);
              for (n = 1; rc == SQLITE_OK && n <= nPage; n++)
              {
                     if (n == nSkip)
                           continue;
 
                     rc = sqlite3PagerGet(p,n,(DbPage**)&pPage);
                     if (!rc)
                     {
                           rc = sqlite3PagerWrite((DbPage*)pPage);
                           sqlite3PagerUnref((DbPage*)pPage);
                     }
              }
       }
 
       // 如果成功,提交事务。
       if (!rc)
       {
              rc = sqlite3BtreeCommit(pbt);
       }
 
       // 如果失败,回滚。
       if (rc)
       {
              sqlite3BtreeRollback(pbt,SQLITE_OK);
       }
 
       // 如果成功,销毁先前的读密钥。并使读密钥等于当前的写密钥。
       if (!rc)
       {
              if (pBlock->ReadKey)
              {
                     sqlite3_free(pBlock->ReadKey);
              }
 
              pBlock->ReadKey = pBlock->WriteKey;
       }
       else // 如果失败,销毁当前的写密钥,并恢复为当前的读密钥。
       {
              if (pBlock->WriteKey)
              {
                     sqlite3_free(pBlock->WriteKey);
              }
              pBlock->WriteKey = pBlock->ReadKey;
       }
 
       // 如果读密钥和写密钥皆为空,就不需要再对页进行编解码。
       // 销毁加密块并移除页的编解码器
       if (!pBlock->ReadKey && !pBlock->WriteKey)
       {
              sqlite3pager_set_codec(p,NULL,NULL);
			  sqlite3CodecFree(pBlock);
       }
 
       return rc;
}
 
// 实现附加密钥到数据库接口
// ================================================================================
int sqlite3CodecAttach(sqlite3 *db,int nDb,int nKeyLen)
{
       int rc = SQLITE_ERROR;
       unsigned char* hKey = 0;
 
       //如果没有指定密匙,可能标识用了主数据库的加密或没加密.
       if (!pKey || !nKeyLen)
       {
              if (!nDb)
              {
                     return SQLITE_OK; //主数据库,没有指定密钥所以没有加密.
              }
              else //附加数据库,使用主数据库的密钥.
              {
                     //获取主数据库的加密块并复制密钥给附加数据库使用
                     LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(sqlite3BtreePager(db->aDb[0].pBt));
 
                     if (!pBlock)
                           return SQLITE_OK; //主数据库没有加密
 
                     if (!pBlock->ReadKey)
                           return SQLITE_OK; //没有加密
 
                     memcpy(pBlock->ReadKey,&hKey,16);
              }
       }
       else //用户提供了密码,从中创建密钥.
       {
              hKey = DeriveKey(pKey,nKeyLen);
       }
 
       //创建一个新的加密块,并将解码器指向新的附加数据库.
       if (hKey)
       {
              LPCryptBlock pBlock = CreateCryptBlock(hKey,sqlite3BtreePager(db->aDb[nDb].pBt)->pageSize,NULL);
              sqlite3pager_set_codec(sqlite3BtreePager(db->aDb[nDb].pBt),pBlock);
              rc = SQLITE_OK;
       }
 
       return rc;
}

#endif //#ifdef SQLITE_HAS_CODEC

7.OK,代码部分完成了,但要使用Sqlite的加密前必须要预添加加SQLITE_HAS_CODEC这个宏,怎么加就不详诉了

8.最后,测试代码


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {
#include "sqlite3.h"
};

#define  SQLITE3_STATIC

extern int sqlite3_key(sqlite3 *db,int nKey);

static int _callback_exec(void * notused,int argc,char ** argv,char ** aszColName)
{
	int i;
	for ( i=0; i<argc; i++ )
	{
		printf( "%s = %s\r\n",aszColName[i],argv[i] == 0 ? "NUL" : argv[i] );
	}

	return 0;
}

int main(int argc,char * argv[])
{
	const char * sSQL;
	char * pErrMsg = 0;
	int ret = 0;
	sqlite3 * db = 0;

	//创建数据库
	ret = sqlite3_open("encrypt.db",&db);

	//添加密码
	ret = sqlite3_key( db,"mypass",strlen("mypass"));

	//在内存数据库中创建表
	sSQL = "create table class(name varchar(20),student);";
	sqlite3_exec( db,sSQL,_callback_exec,&pErrMsg );

	//插入数据
	sSQL = "insert into class values('mem_52911','zhaoyun');";
	sqlite3_exec( db,&pErrMsg );

	//取得数据并显示
	sSQL = "select * from class;";
	sqlite3_exec( db,&pErrMsg );

	//关闭数据库
	sqlite3_close(db);
	db = 0;

	return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SQLite架构简单,又有Json计算能力,有时会承担Json文件/RESTful的计算功能,但SQLite不能直接解析Json文件/RESTful,需要用Java代码硬写,或借助第三方类库,最后再拼成insert语句插入数据表,代码非常繁琐,这里就不展示了。参考前面的代码可知,入库的过程比较麻烦,不能只用SQL,还要借助Java或命令行。SPL是现代的数据计算语言,属于简化的面向对象的语言风格,有对象的概念,可以用点号访问属性并进行多步骤计算,但没有继承重载这些内容,不算彻底的面向对象语言。...
使用Python操作内置数据库SQLite以及MySQL数据库。
破解微信数据库密码,用python导出微信聊天记录
(Unity)SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。本教程将告诉您如何使用 SQLite 编程,并让你迅速上手。.................................
安卓开发,利用SQLite实现登陆注册功能
相比大多数数据库而言,具有等优势,广泛应用于、等领域。
有时候,一个项目只有一个数据库,比如只有SQLite,或者MySQL数据库,那么我们只需要使用一个固定的数据库即可。但是一个项目如果写好了,有多个用户使用,但是多个用户使用不同的数据库,这个时候,我们就需要把软件设计成可以连接多个数据库的模式,用什么数据库,就配置什么数据库即可。4.Users实体类,这个实体类要和数据库一样的,形成一一对应的关系。11.Sqlite数据库,需要在代码里面创建数据库,建立表,再建立数据。8.我们开启MySQL数据库,然后进行调试,看程序的结果。2.安装SqlSugar。
基于Android的背单词软件,功能强大完整。
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统。说白了就是使用起来轻便简单,
Android的简单购物车案例
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库...
Qt设计较为美观好看的登录注册界面(包含SQLite数据库以及TCP通信的应用)
SQLite是用C语言开发的跨平台小型数据库,可嵌入其他开发语言,也可在单机执行。SPL是用Java开发的跨平台的数据计算语言,可嵌入Java,可在单机执行,可以数据计算服务的形式被远程调用。两者的代码都是解释执行的。...
新建库.openDATA_BASE;新建表createtableLIST_NAME(DATA);语法:NAME关键字...<用逗号分割>删除表droptableNAME;查看表.schema查看表信息新建数据insertintoLIST_NAMEvalues();语法:CLASS,PARAMETER...,CLASS是类别,PARAMETER是参数<用逗号分割新建的
importsqlite3classDemo01:def__init__(self):self.conn=sqlite3.connect("sql_demo_001.db")self.cursor1=self.conn.cursor()self.cursor1.execute("select*fromtable_001wherename=?andid=?",('ssss&#0
 在客户端配置文件<configuration>节点下,添加:<connectionStrings>      <add name="localdb" connectionString="Data Source=config/local.db;Version=3;UseUTF16Encoding=True;" providerName="System.Data.SQLite.SQLiteFactory"/&g
提到锁就不得不说到死锁的问题,而SQLite也可能出现死锁。下面举个例子:连接1:BEGIN(UNLOCKED)连接1:SELECT...(SHARED)连接1:INSERT...(RESERVED)连接2:BEGIN(UNLOCKED)连接2:SELECT...(SHARED)连接1:COMMIT(PENDING,尝试获取EXCLUSIVE锁,但还有SHARED锁未释放,返回SQLITE_BUSY)连接2:INSERT...
SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。在使用SQLite前,我们先要搞清楚几个概念:表
设计思想————首先要确定有几个页面、和每个页面的大致布局由于是入门,我也是学习了不是很长的时间,所以项目比较low。。。。第一个页面,也就是打开APP的首页面:今天这个博客,先实现添加功能!:首先对主界面进行布局:其中activity_main.xml的代码为<?xmlversion="1.0"encoding="