VC中设置sqlite3密码时报错!急急急!

bailang20 2009-07-11 09:28:54
加精
各位请教个问题,在sqlite3中设置密码时报错不知道是什么原因。
代码如下:
extern "C"

{

#include "./sqlite3.h"

};

#include <iostream>

using namespace std;


//sqlite3的回调函数

// sqlite 每查到一条记录,就调用一次这个回调

int LoadMyInfo( void * para, int n_column, char ** column_value, char ** column_name )

{

//para是你在 sqlite3_exec 里传入的 void * 参数

//通过para参数,你可以传入一些特殊的指针(比如类指针、结构指针),然后在这里面强制转换成对应的类型(这里面是void*类型,必须强制转换成你的类型才可用)。然后操作这些数据

//n_column是这一条记录有多少个字段 (即这条记录有多少列)

// char ** column_value 是个关键值,查出来的数据都保存在这里,它实际上是个1维数组(不要以为是2维数组),每一个元素都是一个 char * 值,是一个字段内容(用字符串来表示,以\0结尾)

//char ** column_name 跟 column_value是对应的,表示这个字段的字段名称



//这里,我不使用 para 参数。忽略它的存在.



int i;

printf( "记录包含 %d 个字段\n", n_column );

for( i = 0 ; i < n_column; i ++ )

{

printf("字段名:%s 字段值:%s\n", column_name[i], column_value[i] );

}

printf( "------------------\n" );

return 0;

}


const char* gszFile = "c:\\Maindb.db";
const char* gszPass = "ksbaonetlyt2009";

int main( int , char** )

{
sqlite3 * db;

int result;

char * errmsg = NULL;

result = sqlite3_open( gszFile, &db );


if( result != SQLITE_OK )

{

//数据库打开失败
printf( "数据库打开失败\n");

return -1;

}else
printf( "数据库打开成功\n");

int nRet = sqlite3_key(db, gszPass, strlen(gszPass));

if (nRet != SQLITE_OK)
{
printf( "密码设置失败\n");
const char* szError = sqlite3_errmsg(db);
cout << szError;
//exit(0);
}else
printf( "密码设置成功\n");


result = sqlite3_exec( db, "select * from style", LoadMyInfo, NULL, &errmsg );

sqlite3_close( db );

//char c;
getchar();

return 0;
}

报错如下:
数据库打开成功
密码设置失败
Cryptographic provider not available

为什么设置密码时就会这样报错呢。
...全文
1276 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
zc_china 2010-07-15
  • 打赏
  • 举报
回复
我也拿10分
cangzhu 2010-05-31
  • 打赏
  • 举报
回复
Mark 2楼详细 做个记录

每天回帖即可获得10分可用分!小技巧:教您如何更快获得可用分
dronly 2009-08-24
  • 打赏
  • 举报
回复
学习了~~
surepretty 2009-07-14
  • 打赏
  • 举报
回复
mark
Gavin001 2009-07-14
  • 打赏
  • 举报
回复
我一直在用sqlite3
也用在加密
一切都很正常
sqlite3_key函数是打开已加密的数据库时使用的
而不是用来加密
加密使用函数sqlite3_rekey
如果原来数据库没有加密,直接调用sqlite3_rekey加密就行了
如果原数据库已加密,需要先调用sqlite3_key打开数据库
再调用sqlite3_rekey重置密码
lg198422 2009-07-13
  • 打赏
  • 举报
回复
顶一个
做鸡真好吃 2009-07-13
  • 打赏
  • 举报
回复
mark~
yangyunzhao 2009-07-12
  • 打赏
  • 举报
回复
sqlite不支持加密,而且现有的第三方加密都不怎么样
red_berries 2009-07-11
  • 打赏
  • 举报
回复
记着好像是公布的源码好像不支持加密码,加密的功能好像要花钱,记不太清了
kingbs_fly 2009-07-11
  • 打赏
  • 举报
回复
sqlite估计并不一定支持设置密码
猞猁狲 2009-07-11
  • 打赏
  • 举报
回复
const char* szError = sqlite3_errmsg(db);

这句的szError中Sqlite3的提示错误信息是什么!

还有这篇文章 《SQLITE3 使用总结一下-zz董淳光》你看了吗,你面说要加个什么宏,你自己去看看
我的项目里也是用了SQLITE3,不过我没有用过加密


SQLITE3 使用总结(5)


五、给数据库加密
前面所说的内容网上已经有很多资料,虽然比较零散,但是花点时间也还是可以找到的。现在要说的这个——数据库加密,资料就很难找。也可能是我操作水平不够,找不到对应资料。但不管这样,我还是通过网上能找到的很有限的资料,探索出了给sqlite数据库加密的完整步骤。
这里要提一下,虽然 sqlite 很好用,速度快、体积小巧。但是它保存的文件却是明文的。若不信可以用 NotePad 打开数据库文件瞧瞧,里面 insert 的内容几乎一览无余。这样赤裸裸的展现自己,可不是我们的初衷。当然,如果你在嵌入式系统、智能手机上使用 sqlite,最好是不加密,因为这些系统运算能力有限,你做为一个新功能提供者,不能把用户有限的运算能力全部花掉。
Sqlite为了速度而诞生。因此Sqlite本身不对数据库加密,要知道,如果你选择标准AES算法加密,那么一定有接近50%的时间消耗在加解密算法上,甚至更多(性能主要取决于你算法编写水平以及你是否能使用cpu提供的底层运算能力,比如MMX或sse系列指令可以大幅度提升运算速度)。
Sqlite免费版本是不提供加密功能的,当然你也可以选择他们的收费版本,那你得支付2000块钱,而且是USD。我这里也不是说支付钱不好,如果只为了数据库加密就去支付2000块,我觉得划不来。因为下面我将要告诉你如何为免费的Sqlite扩展出加密模块——自己动手扩展,这是Sqlite允许,也是它提倡的。
那么,就让我们一起开始为 sqlite3.c 文件扩展出加密模块。

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只留了接口而已,并没有给出实现。
下面就让我来实现这些接口。

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 文件,而且函数要按我前面定义的要求来做。
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 );
来完成密码清空功能。


oyljerry 2009-07-11
  • 打赏
  • 举报
回复
sqlite估计并不一定支持设置密码
baohui54883 2009-07-11
  • 打赏
  • 举报
回复
来帮楼主顶
eWong2016 2009-07-11
  • 打赏
  • 举报
回复
今天又下雨,楼主问题解决没有
eWong2016 2009-07-11
  • 打赏
  • 举报
回复
帮你顶啊。。。。。。。。。。。
lwtsea 2009-07-11
  • 打赏
  • 举报
回复
路过,学习
lihuaibin 2009-07-11
  • 打赏
  • 举报
回复
好长呀
csbinchina 2009-07-11
  • 打赏
  • 举报
回复
帮顶。。
luxiao0816 2009-07-11
  • 打赏
  • 举报
回复
在刚刚处理完过第一个自自己开始的全职Oracle服务的case不几天,一个朋友推荐了一个业务给我,一个市级数据库在进行导入导出的过程中,遭受突然的死机,再次开机以后,数据库瘫痪,不能运行。本来这样的系统瘫痪事故是比较容易解决的,不过客户的数据库特别的大,对于用户来说,根本就没有做好备份的准备,更重要的是,用户无法承受极长的停机时间。项目是由朋友负责维护,当数据库遭受到这样的打击的时候,他第一时间通知了我。

wenxiong1234 2009-07-11
  • 打赏
  • 举报
回复
我不怎么明白。

4,012

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 数据库
社区管理员
  • 数据库
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧