天天看點

Sqlite3 加密完善修改

前言

本文主要是介紹Sqlite 3.7.17版本加密功能添加。本文的代碼是基于網上流傳的一份樣本,并結合國外的一份加密開源代碼做了完善修改。

發表此文的原因

本人需要對sqlite的資料庫進行加密,使用了網絡流傳的一份代碼,發現有BUG。研究了一下,發現因為加密的時候把sqlite的資料庫檔案完全都加密了,連一些重要的版本等等資訊都給加密,導緻資料庫檔案讀取的時候pagesize這種重要的參數出現随機數,sqlite引擎在處理這個随機數的時候,大部分是能相容的,小部分機率會導緻資料庫檔案打不開。而在實際使用中,這種小機率引起的問題是非常嚴重的。并且網上流傳的代碼有明顯的缺陷,有一處拷貝記憶體的地方,目标和源都搞反了。

原理介紹

網上有介紹兩種方法:

     方案一:(徹底解決方案)修改sqlite源碼,使opendatase讀取的pagesize無效,在設定好資料庫密鑰以後,第一次讀取資料時重新計算pagesize;

     方案二:(針對加密的修補方案)修改sqlite3_key的加密實作,在設定密鑰時,解密讀取資料庫的頭資訊,讀取解密後的pagesize,再把這個正确的pagesize設定回去;

本人提出新的一種方法:

     保留檔案頭法:檔案頭128位元組不加密,這樣讀取的pagesize就應該不會有錯!

提出這個方法的原因:sqlite的源碼版本總會前進改變,pagesize的位置今天是16,沒準下個版本會換到什麼位置,是以網上介紹的“方案二”不是長久辦法;而修改sqlite的源碼,這種實在是太暴力了,以後更新sqlite代碼時候,肯定不可取。

這個方法使用128位元組,是因為這樣能覆寫大部分檔案頭重要資訊了,而又沒洩漏太多的資料庫重要資料。實用又簡單。

這個方法經過測試驗證,暫時沒有發現什麼問題。如果誰發現有漏洞,歡迎探讨。

下面貼出關鍵的算法:

void* sqlite3Codec(void *pCodec, void *data, Pgno nPageNum, int nMode)
{
	void *codecptr = data;
	LPCryptBlock pBlock = (LPCryptBlock) pCodec;
	int len = 0;
	if (pCodec == NULL) 
		return data;
	switch(nMode)
	{
	case 0: 
	case 2:
	case 3: 
		if (!pBlock->ReadKey)
			break;
		len = 0 - (pBlock->PageSize/4);
		if(nPageNum == 1)
		{
			len += no_codec_header_size/4;
			(BYTE *)codecptr += no_codec_header_size;
		}
		call_Decrypt((int *)codecptr, len, (int *)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;
		codecptr = data;
		if(nPageNum == 1)
		{
			len -= no_codec_header_size/4;
			(BYTE *)codecptr += no_codec_header_size;
		}
		call_Encrypt((int *)codecptr , len, (int *)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;
		codecptr = data;
		if(nPageNum == 1)
		{
			len -= no_codec_header_size/4;
			(BYTE *)codecptr += no_codec_header_size;
		}
		call_Encrypt((int *)codecptr, len, (int *)pBlock->ReadKey);
		break;
	}
	return data;
}