zoukankan      html  css  js  c++  java
  • Sqlite 3.7.14.1 xxtea 加密算法

    http://blog.csdn.net/wzq9706/article/details/8133314

    最近在研空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 data_len, unsigned char * key, 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 data_len, unsigned char * key, unsigned int len_of_key)
    {
    	return 0;
    }
    
    // 其它算法,未实现
    // ==============================================================================
    int sqlite3_dencrypt( unsigned char * pData, unsigned int data_len, unsigned char * key, unsigned int len_of_key)
    {
    	
    	return 0;
    }
    
    // ==============================================================================
    void * sqlite3Codec(void *pArg, void *data, int nPageNum, 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 , len, pBlock->WriteKey);  
            break;  
    
        case 7:
    		// 加密事务文件的页
            if (!pBlock->ReadKey) break;  
            memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);  
            data = pBlock->Data + CRYPT_OFFSET;  
            len = pBlock->PageSize / 4;  
            xxtea(data, len, pBlock->ReadKey);  
            break; 
    		   /*
           case 3: //载入一个页
                  if (!pBlock->ReadKey)
                         break;
     
    			  memcpy(pBlock->Data + CRYPT_OFFSET, data, 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, data, pBlock->PageSize);
                  data = pBlock->Data + CRYPT_OFFSET;
                  dwPageSize = pBlock->PageSize;
     
    			  // 加密
                  sqlite3_encrypt((BYTE*)data, dwPageSize, pBlock->WriteKey, DB_KEY_LENGTH_BYTE); 
     
                  break;
     
           case 7: 
    			  // 加密事务文件的页
                  // 在正常环境下, 读密钥和写密钥相同. 当数据库是被重新加密的,读密钥和写密钥未必相同.
                  // 回滚事务必要用数据库文件的原始密钥写入.因此,当一次回滚被写入,总是用数据库的读密钥,
                  // 这是为了保证与读取原始数据的密钥相同.
                  //
                  if (!pBlock->ReadKey)
                         break;
     
                  memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
                  data = pBlock->Data + CRYPT_OFFSET;
                  dwPageSize = pBlock->PageSize;
     
                  sqlite3_encrypt((BYTE*)data, dwPageSize, pBlock->ReadKey, 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, int nPageSize, LPCryptBlock pExisting)
    {
    	LPCryptBlock pBlock;
    
    	if (!pExisting) //创建新加密块
    	{
    		pBlock = sqlite3_malloc(sizeof(CryptBlock));
    		memset(pBlock, 0, 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, 0, 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, pKey, 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, 0, pKey, nKey);
    }
     
     // 实现解密接口
    int sqlite3_rekey(sqlite3 *db, const void *pKey, 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, NULL);
    			  sqlite3CodecFree(pBlock);
           }
     
           return rc;
    }
     
    // 实现附加密钥到数据库接口
    // ================================================================================
    int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, 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), sqlite3Codec, sqlite3CodecFree, 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, const void *pKey, 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, 0, &pErrMsg );
    
    	//插入数据
    	sSQL = "insert into class values('mem_52911', 'zhaoyun');";
    	sqlite3_exec( db, sSQL, _callback_exec, 0, &pErrMsg );
    
    	//取得数据并显示
    	sSQL = "select * from class;";
    	sqlite3_exec( db, sSQL, _callback_exec, 0, &pErrMsg );
    
    	//关闭数据库
    	sqlite3_close(db);
    	db = 0;
    
    	return 0;
    }


  • 相关阅读:
    dubbo服务集群
    Caused by: java.sql.SQLException: No value specified for parameter 2
    logstash 解析windows iis日志
    maven fastdfs_client.jar找不到问题解决
    logstsha 发送数据到数据节点
    redis被黑客入侵
    logstash 自定义正则
    Caused by: java.sql.SQLException: Generated keys not requested
    BeanFactory not initialized or already closed
    Exception in thread "main" java.lang.UnsupportedClassVersionError: org/apache/maven/cli/MavenCli : U
  • 原文地址:https://www.cnblogs.com/iapp/p/3631758.html
Copyright © 2011-2022 走看看