sqlite的源代码加密,以及其它一些文章
一、 给数据库加密
前面所说的内容网上已经有很多资料,虽然比较零散,但是花点时间也还是可以找到的。现在要说的这个——数据库加密,资料就很难找。也可能是我操作水平不够,找不到对应资料。但不管这样,我还是通过网上能找到的很有限的资料,探索出了给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的源代码加密,以及其它一些文章的更多相关文章
- .NET+Sqlite如何支持加密
.NET+Sqlite如何支持加密 Sqlite SQLite 来源于公共领域 SQLite Is Public Domain. 确保代码不会受到任何专有或许可内容的污染,没有任何来自互联网上的未知来 ...
- 如何使用NET Reactor为您的.Net(C#,VB.Net) 源代码加密
前言 VS开发的源代码安全性,是很多开发者头痛的事情.于是保护好源代码便成了开发者们最关心的事情之一了. 在网上搜一搜,很多有不少的第三方工具可以为源代码加密.加密方式不外乎就是混淆,加壳. 理论上, ...
- SQLite数据库的加密【转】
1.创建空的SQLite数据库. //数据库名的后缀你可以直接指定,甚至没有后缀都可以 //方法一:创建一个空sqlite数据库,用IO的方式 FileStream fs = File.Create( ...
- Sqlite数据库的加密
最近在做一个winform的程序,考虑用Sqlite的数据库,小巧而实用,比Access强多了,不过需要加密,不过free版本没有实现加密,有一些c++的实现:比如:http://www.sqlite ...
- PHP源代码加密
加密软件(php_screw) >下载网站:http://sourceforge.net/projects/php-screw/ >描述:php文件通常以文本格式存贮在服务器端, 很容易被 ...
- [转].net reactor 学习系列(五)---源代码加密程序
.NET Reactor使用教程(加密源代码示例) 1.打开 Eziriz .NET Reactor,主界面如图1所示: 图1 2.单击 Main Assembly 右边的 Open,选择要加密的软件 ...
- system.data.sqlite的源代码下载
帮助文档 http://system.data.sqlite.org/index.html/doc/trunk/www/index.wiki 历史版本https://system.data.sqlit ...
- (转)SQLite数据库的加密
1.创建空的SQLite数据库. //数据库名的后缀你可以直接指定,甚至没有后缀都可以 //方法一:创建一个空sqlite数据库,用IO的方式 FileStream fs = File.Create( ...
- 一种简单有效的VBA源代码加密办法,支持64位宿主,适用于大部分VBA代码加密
原始出处:http://www.cnblogs.com/Charltsing/p/EncryptVBACode.html VBA代码加密是个老生常谈的问题,自从本人的VBA Dumper发布之后,在O ...
随机推荐
- eclipse和android studio导入工程的错误
eclipse中导入工程,需要注意导入的工程是什么,android 工程和java工程是有区别的.如果导入错误了,调起来也比较麻烦.因为入口错了呀. 特别在android studio工程,从其它人的 ...
- 查看SQL server 2008 R2 的Service Package 版本号(同样适用于SQL Server 2005)
在SQL Server 中新建一个查询(new Query),然后输入下面的SQL脚本,即可看到当前的数据库的Service Package (补丁包)的版本号 select serverproper ...
- tomcat启动正常,404. Eclipse没有正确部署工程项目
http://blog.csdn.net/lynn_wgr/article/details/7751228 在eclipse中新建的Dynamic Web Project.写好代码后,选择Run on ...
- Saltstack安装配置(一)
一.服务端和客户端安装 1.下载epel源 http://mirrors.zju.edu.cn/epel/6/ #wget http://mirrors.zju.edu.cn/epel/6/x86_6 ...
- Apache+Tomcat +mod_proxy集群负载均衡及session
序言: 在玩Apache+Tomcat +mod_jk集群负载均衡及session的时候发现,还有一种方式可以实现,就是网上各位大牛们说的mod_proxy反向代理. 实在弄的我的知识细胞洋洋.实 ...
- Js获取fileupload的绝对路径时总是的到C:\fakepath\+文件名称的 解决方案
解决方法: Internet选项->安全->自定义级别->将文件下载到服务器时包含本地目录路径 启用就可以了.
- CentOS系统Apache服务器优化详解
1.Apache优化 Apache能够在CentOS系统正常运行.但是,对于访问量稍大的站点,Apache的这些默认配置是无法满足需求的,我们仍需调整Apache的一些参数,使Apache能够在大访问 ...
- PHP优化小结
1.echo 比 print 快,并且使用echo的多重参数(指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2.如果使用echo $str1.$str2 就会需要 PHP 引擎 ...
- yii2源码学习笔记(十三)
模型类DynamicModel主要用于实现模型内的数据验证yii2\base\DynamicModel.php <?php /** * @link http://www.yiiframework ...
- c 的ui
你这些问题是必须要搞清楚的. 1. 首先,UI的实现在不同的操作系统平台上是有所不同的.Windows的UI是内置于操作系统的,Linux/Unix的UI则是通过一个应用程序实现的.由此看来,变成语言 ...