各位请教个问题,在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为什么设置密码时就会这样报错呢。

解决方案 »

  1.   

    sqlite估计并不一定支持设置密码
      

  2.   

    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 );
    来完成密码清空功能。
      

  3.   

    sqlite估计并不一定支持设置密码
      

  4.   

    在刚刚处理完过第一个自自己开始的全职Oracle服务的case不几天,一个朋友推荐了一个业务给我,一个市级数据库在进行导入导出的过程中,遭受突然的死机,再次开机以后,数据库瘫痪,不能运行。本来这样的系统瘫痪事故是比较容易解决的,不过客户的数据库特别的大,对于用户来说,根本就没有做好备份的准备,更重要的是,用户无法承受极长的停机时间。项目是由朋友负责维护,当数据库遭受到这样的打击的时候,他第一时间通知了我。 
      

  5.   

    sqlite不支持加密,而且现有的第三方加密都不怎么样
      

  6.   

    我一直在用sqlite3
    也用在加密
    一切都很正常
    sqlite3_key函数是打开已加密的数据库时使用的
    而不是用来加密
    加密使用函数sqlite3_rekey
    如果原来数据库没有加密,直接调用sqlite3_rekey加密就行了
    如果原数据库已加密,需要先调用sqlite3_key打开数据库
    再调用sqlite3_rekey重置密码
      

  7.   

    Mark 2楼详细 做个记录每天回帖即可获得10分可用分!小技巧:教您如何更快获得可用分