sqlite的原始碼加密,以及其它一些文章

來源:互聯網
上載者:User

標籤:

一、       給資料庫加密

前面所說的內容網上已經有很多資料,雖然比較零散,但是花點時間也還是可以找到的。現在要說的這個——資料庫加密,資料就很難找。也可能是我操作水平不夠,找不到對應資料。但不管這樣,我還是通過網上能找到的很有限的資料,探索出了給sqlite資料庫加密的完整步驟。

這裡要提一下,雖然 sqlite 很好用,速度快、體積小巧。但是它儲存的檔案卻是明文的。若不信可以用 NotePad 開啟資料庫檔案瞧瞧,裡面 insert 的內容幾乎一覽無餘。這樣赤裸裸的展現自己,可不是我們的初衷。當然,如果你在嵌入式系統、智能手機上使用 sqlite,最好是不加密,因為這些系統運算能力有限,你做為一個新功能提供者,不能把使用者有限的運算能力全部花掉。

Sqlite為了速度而誕生。因此Sqlite本身不對資料庫加密,要知道,如果你選擇標準AES演算法加密,那麼一定有接近50%的時間消耗在加解密演算法上,甚至更多(效能主要取決於你演算法編寫水平以及你是否能使用cpu提供的底層運算能力,比如MMX或sse系列指令可以大幅度提升運算速度)。

Sqlite免費版本是不提供加密功能的,當然你也可以選擇他們的收費版本,那你得支付2000塊錢,而且是USD。我這裡也不是說支付錢不好,如果只為了資料庫加密就去支付2000塊,我覺得划不來。因為下面我將要告訴你如何為免費的Sqlite擴充出加密模組——自己動手擴充,這是Sqlite允許,也是它提倡的。

那麼,就讓我們一起開始為 sqlite3.c 檔案擴充出加密模組。

 

i.1          必要的宏

通過閱讀 Sqlite 代碼(當然沒有全部閱讀完,6萬多行代碼,沒有一行是我習慣的風格,我可沒那麼多眼神去看),我搞清楚了兩件事:

Sqlite是支援加密擴充的;

需要 #define 一個宏才能使用加密擴充。

這個宏就是  SQLITE_HAS_CODEC。

你在代碼最前面(也可以在 sqlite3.h 檔案第一行)定義:

#ifndef SQLITE_HAS_CODEC

#define SQLITE_HAS_CODEC

#endif

 

如果你在代碼裡定義了此宏,但是還能夠正常編譯,那麼應該是操作沒有成功。因為你應該會被編譯器提示有一些函數無法連結才對。如果你用的是 VC 2003,你可以在“解決方案”裡右鍵點擊你的工程,然後選“屬性”,找到“C/C++”,再找到“命令列”,在裡面手工添加“/D "SQLITE_HAS_CODEC"”。

定義了這個宏,一些被 Sqlite 故意屏蔽掉的代碼就被使用了。這些代碼就是加解密的介面。

嘗試編譯,vc會提示你有一些函數無法連結,因為找不到他們的實現。

如果你也用的是VC2003,那麼會得到下面的提示:

error LNK2019: 無法解析的外部符號 _sqlite3CodecGetKey ,該符號在函數 _attachFunc 中被引用

error LNK2019: 無法解析的外部符號 _sqlite3CodecAttach ,該符號在函數 _attachFunc 中被引用

error LNK2019: 無法解析的外部符號 _sqlite3_activate_see ,該符號在函數 _sqlite3Pragma 中被引用

error LNK2019: 無法解析的外部符號 _sqlite3_key ,該符號在函數 _sqlite3Pragma 中被引用

fatal error LNK1120: 4 個無法解析的外部命令

 

這是正常的,因為Sqlite只留了介面而已,並沒有給出實現。

下面就讓我來實現這些介面。

 

i.2          自己實現加解密介面函數

如果真要我從一份 www.sqlite.org 網上down下來的 sqlite3.c 檔案,直接摸索出這些介面的實現,我認為我還沒有這個能力。

好在網上還有一些代碼已經實現了這個功能。通過參照他們的代碼以及不斷編譯中vc給出的錯誤提示,最終我把整個介面整理出來。

實現這些預留介面不是那麼容易,要重頭說一次怎麼回事很困難。我把代碼都寫好了,直接把他們按我下面的說明拷貝到 sqlite3.c 檔案對應地方即可。我在下面也提供了sqlite3.c 檔案,可以直接參考或取下來使用。

 

這裡要說一點的是,我另外建立了兩個檔案:crypt.c和crypt.h。

其中crypt.h如此定義:

#ifndef  DCG_SQLITE_CRYPT_FUNC_

#define  DCG_SQLITE_CRYPT_FUNC_

/***********

董淳光寫的 SQLITE 加密關鍵函數庫

***********/

 

/***********

關鍵加密函數

***********/

int My_Encrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key );

 

/***********

關鍵解密函數

***********/

int My_DeEncrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key );

 

#endif

 

 

其中的 crypt.c 如此定義:

#include "./crypt.h"

#include "memory.h"

/***********

關鍵加密函數

***********/

int My_Encrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key )

{

return 0;

}

 

/***********

關鍵解密函數

***********/

int My_DeEncrypt_Func( unsigned char * pData, unsigned int data_len, const char * key, unsigned int len_of_key )

{

return 0;

}

 

這個檔案很容易看,就兩函數,一個加密一個解密。傳進來的參數分別是待處理的資料、資料長度、密鑰、密鑰長度。

處理時直接把結果作用於 pData 指標指向的內容。

你需要定義自己的加解密過程,就改動這兩個函數,其它部分不用動。擴充起來很簡單。

這裡有個特點,data_len 一般總是 1024 位元組。正因為如此,你可以在你的演算法裡使用一些特定長度的密碼編譯演算法,比如AES要求被加密資料一定是128位(16位元組)長。這個1024不是碰巧,而是 Sqlite 的頁定義是1024位元組,在sqlite3.c檔案裡有定義:

# define SQLITE_DEFAULT_PAGE_SIZE 1024

你可以改動這個值,不過還是建議沒有必要不要去改它。

 

上面寫了兩個擴充函數,如何把擴充函數跟 Sqlite 掛接起來,這個過程說起來比較麻煩。我直接貼代碼。

分3個步驟。

首先,在 sqlite3.c 檔案頂部,添加下面內容:

 

#ifdef SQLITE_HAS_CODEC

#include "./crypt.h"

/***********

用於在 sqlite3 最後關閉時釋放一些記憶體

***********/

void sqlite3pager_free_codecarg(void *pArg);

#endif

這個函數之所以要在 sqlite3.c 開頭聲明,是因為下面在 sqlite3.c 裡面某些函數裡要插入這個函數調用。所以要提前聲明。

 

其次,在sqlite3.c檔案裡搜尋“sqlite3PagerClose”函數,要找到它的實現代碼(而不是聲明代碼)。

實現代碼裡一開始是:

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT

  /* A malloc() cannot fail in sqlite3ThreadData() as one or more calls to

  ** malloc() must have already been made by this thread before it gets

  ** to this point. This means the ThreadData must have been allocated already

  ** so that ThreadData.nAlloc can be set.

  */

  ThreadData *pTsd = sqlite3ThreadData();

  assert( pPager );

  assert( pTsd && pTsd->nAlloc );

#endif

 

需要在這部分後面緊接著插入:

 

#ifdef SQLITE_HAS_CODEC

  sqlite3pager_free_codecarg(pPager->pCodecArg);

#endif

 

這裡要注意,sqlite3PagerClose 函數大概也是 3.3.17版本左右才改名的,以前版本裡是叫 “sqlite3pager_close”。因此你在老版本sqlite代碼裡搜尋“sqlite3PagerClose”是搜不到的。

類似的還有“sqlite3pager_get”、“sqlite3pager_unref”、“sqlite3pager_write”、“sqlite3pager_pagecount”等都是老版本函數,它們在 pager.h 檔案裡定義。新版本對應函數是在 sqlite3.h 裡定義(因為都合并到 sqlite3.c和sqlite3.h兩檔案了)。所以,如果你在使用老版本的sqlite,先看看 pager.h 檔案,這些函數不是消失了,也不是新蹦出來的,而是老版本函數改名得到的。

 

最後,往sqlite3.c 檔案下找。找到最後一行:

 

/************** End of main.c ************************************************/

 

在這一行後面,接上本文最下面的程式碼片段。

這些代碼很長,我不再解釋,直接接上去就得了。

唯一要提的是 DeriveKey 函數。這個函數是對密鑰的擴充。比如,你要求密鑰是128位,即是16位元組,但是如果使用者只輸入 1個位元組呢?2個位元組呢?或輸入50個位元組呢?你得對密鑰進行擴充,使之符合16位元組的要求。

DeriveKey 函數就是做這個擴充的。有人把接收到的密鑰求md5,這也是一個辦法,因為md5運算結果固定16位元組,不論你有多少字元,最後就是16位元組。這是md5演算法的特點。但是我不想用md5,因為還得為它添加包含一些 md5 的.c或.cpp檔案。我不想這麼做。我自己寫了一個演算法來擴充密鑰,很簡單的演算法。當然,你也可以使用你的擴充方法,也而可以使用 md5 演算法。只要修改 DeriveKey 函數就可以了。

在 DeriveKey 函數裡,只管申請空間構造所需要的密鑰,不需要釋放,因為在另一個函數裡有釋放過程,而那個函數會在資料庫關閉時被調用。參考我的 DeriveKey 函數來申請記憶體。

 

這裡我給出我已經修改好的 sqlite3.c 和 sqlite3.h 檔案。

如果太懶,就直接使用這兩個檔案,編譯肯定能通過,運行也正常。當然,你必須按我前面提的,建立 crypt.h 和 crypt.c 檔案,而且函數要按我前面定義的要求來做。

 

 

i.3          加密使用方法:

現在,你代碼已經有了加密功能。

你要把加密功能給用上,除了改 sqlite3.c 檔案、給你工程添加 SQLITE_HAS_CODEC 宏,還得修改你的資料庫調用函數。

前面提到過,要開始一個資料庫操作,必須先 sqlite3_open 。

加解密過程就在 sqlite3_open 後面操作。

假設你已經 sqlite3_open 成功了,緊接著寫下面的代碼:

      int i;

//添加、使用密碼      

      i =  sqlite3_key( db, "dcg", 3 );

      //修改密碼

      i =  sqlite3_rekey( db, "dcg", 0 );

 

用 sqlite3_key 函數來提交密碼。

第1個參數是 sqlite3 * 類型變數,代表著用 sqlite3_open 開啟的資料庫(或建立資料庫)。

第2個參數是密鑰。

第3個參數是密鑰長度。

用 sqlite3_rekey 來修改密碼。參數含義同 sqlite3_key。

 

實際上,你可以在sqlite3_open函數之後,到 sqlite3_close 函數之前任意位置調用 sqlite3_key 來設定密碼。

但是如果你沒有設定密碼,而資料庫之前是有密碼的,那麼你做任何操作都會得到一個傳回值:SQLITE_NOTADB,並且得到錯誤提示:“file is encrypted or is not a database”。

只有當你用 sqlite3_key 設定了正確的密碼,資料庫才會正常工作。

如果你要修改密碼,前提是你必須先 sqlite3_open 開啟資料庫成功,然後 sqlite3_key 設定密鑰成功,之後才能用 sqlite3_rekey 來修改密碼。

如果資料庫有密碼,但你沒有用 sqlite3_key 設定密碼,那麼當你嘗試用 sqlite3_rekey 來修改密碼時會得到 SQLITE_NOTADB 傳回值。

如果你需要清空密碼,可以使用:

//修改密碼

      i =  sqlite3_rekey( db, NULL, 0 );

來完成密碼清空功能。

 

 

i.4         sqlite3.c 最後添加程式碼片段

 

 

/***

董淳光定義的加密函數

***/

#ifdef SQLITE_HAS_CODEC

 

/***

加密結構

***/

#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

 

http://www.cnblogs.com/qiubole/archive/2007/08/30/875903.html

http://www.cnblogs.com/qiubole/category/103553.html

 

sqlite的原始碼加密,以及其它一些文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.