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; }