SQLite 3.7.13的加密解密(四)—— 挂接加密解密函数

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介:
把crypt.c中实现的加密解密函数挂接到sqlite3.c中,并且实现前面编译提示的未实现的函数。在sqlite3.c的最后一行的后面,添加如下代码:
#ifdef  SQLITE_HAS_CODEC
#include "crypt.h"
 
/***
 加密结构
 ***/
#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
 
/*** 下面是编译时提示缺少的函数 ***/
/** 这个函数不需要做任何处理,获取密钥的部分在下面 DeriveKey 函数里实现 **/
void  sqlite3CodecGetKey(sqlite3* db,  int nDB,  void** Key,  int* nKey) {
        return;
}
 
/*被 sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.*/
int  sqlite3CodecAttach(sqlite3 *db,  int nDb,  const  void *pKey,  int nKeyLen);
 
/**
 这个函数好像是  sqlite 3.3.17前不久才加的,以前版本的 sqlite里没有看到这个函数
 这个函数我还没有搞清楚是做什么的,它里面什么都不做直接返回,对加解密没有影响
 **/
void  sqlite3_activate_see( const  char* right) {
        return;
}
 
int  sqlite3_key(sqlite3 *db,  const  void *pKey,  int nKey);
int  sqlite3_rekey(sqlite3 *db,  const  void *pKey,  int nKey);
 
/***
 下面是上面的函数的辅助处理函数
 ***/
// 从用户提供的缓冲区中得到一个加密密钥
// 用户提供的密钥可能位数上满足不了要求,使用这个函数来完成密钥扩展
static  unsigned  char *  DeriveKey( const  void *pKey,  int nKeyLen);
//创建或更新一个页的加密算法索引.此函数会申请缓冲区.

static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager,

              LPCryptBlock pExisting);
//加密/解密函数, 被pager调用

void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, int nMode);

//设置密码函数
int __stdcall  sqlite3_key_interop(sqlite3 *db,  const  void *pKey,  int nKeySize);
// 修改密码函数
int __stdcall  sqlite3_rekey_interop(sqlite3 *db,  const  void *pKey,  int nKeySize);
//销毁一个加密块及相关的缓冲区,密钥.
static  void  DestroyCryptBlock(LPCryptBlock pBlock);

static void * sqlite3pager_get_codecarg(Pager *pPager);

void  sqlite3pager_set_codec( Pager *pPager,
               void *(*xCodec)( void*,  void*, Pgno,  int),  void *pCodecArg);
 
//加密/解密函数, 被pager调用

void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, int nMode) {

       LPCryptBlock pBlock = (LPCryptBlock) pArg;
        unsigned  int dwPageSize = 0;
 
        if (!pBlock)
               return data;
       // 确保pager的页长度和加密块的页长度相等.如果改变,就需要调整.
        if (nMode != 2) {

              PgHdr *pageHeader;

 

              pageHeader = DATA_TO_PGHDR(data);

              if (pageHeader->pPager->pageSize != pBlock->PageSize) {

                     CreateCryptBlock(0, pageHeader->pPager, pBlock);

              }
       }
 
        switch (nMode) {

       case 0: // Undo a "case 7" journal file encryption

        case 2: //重载一个页
        case 3: //载入一个页
               if (!pBlock->ReadKey)
                      break;
 

              dwPageSize = pBlock->PageSize;

 

              My_DeEncrypt_Func(data, dwPageSize, pBlock->ReadKey,

                           DB_KEY_LENGTH_BYTE); /*调用我的解密函数*/

 
               break;
 
        case 6: //加密一个主数据库文件的页
               if (!pBlock->WriteKey)
                      break;
 

              memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);

              data = pBlock->Data + CRYPT_OFFSET;

              dwPageSize = pBlock->PageSize;

 

              My_Encrypt_Func(data, dwPageSize, pBlock->WriteKey, DB_KEY_LENGTH_BYTE); /*调用我的加密函数*/

 
               break;
 
        case 7: //加密事务文件的页
              /*在正常环境下, 读密钥和写密钥相同. 当数据库是被重新加密的,读密钥和写密钥未必相同.
               回滚事务必要用数据库文件的原始密钥写入.因此,当一次回滚被写入,总是用数据库的读密钥,
               这是为了保证与读取原始数据的密钥相同.
               */
               if (!pBlock->ReadKey)
                      break;
 

              memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);

              data = pBlock->Data + CRYPT_OFFSET;

              dwPageSize = pBlock->PageSize;

 

              My_Encrypt_Func(data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE); /*调用我的加密函数*/

 
               break;
       }
 
        return data;
}
 
//销毁一个加密块及相关的缓冲区,密钥.
static  void  DestroyCryptBlock(LPCryptBlock pBlock)
{
       //销毁读密钥.
        if (pBlock->ReadKey) {
              sqliteFree(pBlock->ReadKey);
       }
 
//如果写密钥存在并且不等于读密钥,也销毁.
 

       if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey) {

              sqliteFree(pBlock->WriteKey);
       }
 
        if (pBlock->Data) {
              sqliteFree(pBlock->Data);
       }
 
       //释放加密块.
       sqliteFree(pBlock);
}
 

static void * sqlite3pager_get_codecarg(Pager *pPager)

{

       return (pPager->xCodec) ? pPager->pCodecArg : NULL;

}
 
// 从用户提供的缓冲区中得到一个加密密钥
 
static  unsigned  char *  DeriveKey( const  void *pKey,  int nKeyLen)
{
        unsigned  char * hKey =  NULL;
        int j;
 

       if (pKey == NULL || nKeyLen == 0)

       {
               return  NULL;
       }
 

       hKey = sqliteMalloc(DB_KEY_LENGTH_BYTE + 1);

        if (hKey ==  NULL)
       {
               return  NULL;
       }
 

       hKey[DB_KEY_LENGTH_BYTE] = 0;

 

       if (nKeyLen < DB_KEY_LENGTH_BYTE)

       {

              memcpy(hKey, pKey, nKeyLen); //先拷贝得到密钥前面的部分

              j = DB_KEY_LENGTH_BYTE - nKeyLen;

 
              //补充密钥后面的部分

              memset(hKey + nKeyLen, DB_KEY_PADDING, j);

       }
        else
       {
              //密钥位数已经足够,直接把密钥取过来

              memcpy(hKey, pKey, DB_KEY_LENGTH_BYTE);

       }
 
        return hKey;
}
 
//创建或更新一个页的加密算法索引.此函数会申请缓冲区.

static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager,

              LPCryptBlock pExisting)
 
{
       LPCryptBlock pBlock;
 
        if (!pExisting) //创建新加密块
       {

              pBlock = sqliteMalloc(sizeof(CryptBlock));

               memset(pBlock, 0,  sizeof(CryptBlock));
              pBlock->ReadKey = hKey;
              pBlock->WriteKey = hKey;
              pBlock->PageSize = pager-> pageSize;
              pBlock->Data = ( unsigned  char*) sqliteMalloc(
                           pBlock->PageSize + CRYPT_OFFSET);
       }
        else //更新存在的加密块
       {

              pBlock = pExisting;

 

              if (pBlock->PageSize != pager->pageSize && !pBlock->Data) {

                     sqliteFree(pBlock->Data);
                     pBlock->PageSize = pager-> pageSize;
 
                     pBlock->Data = ( unsigned  char*) sqliteMalloc(
                                  pBlock->PageSize + CRYPT_OFFSET);
              }
       }
 
        memset(pBlock->Data, 0, pBlock->PageSize + CRYPT_OFFSET);
 
        return pBlock;
}
 
/*

 ** Set the codec for this pager

 */
void  sqlite3pager_set_codec( Pager *pPager,  void *(*xCodec)( void*,  void*, Pgno,  int),  void *pCodecArg)
{

       pPager->xCodec = xCodec;

       pPager->pCodecArg = pCodecArg;

}
 
int  sqlite3_key(sqlite3 *db,  const  void *pKey,  int nKey)
{

       return sqlite3_key_interop(db, pKey, nKey);

}
 
int  sqlite3_rekey(sqlite3 *db,  const  void *pKey,  int nKey)
{

       return sqlite3_rekey_interop(db, pKey, nKey);

}
 
/*被 sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.*/
int  sqlite3CodecAttach(sqlite3 *db,  int nDb,  const  void *pKey,  int nKeyLen)
{
        int rc =  SQLITE_ERROR;
        unsigned  char* hKey = 0;
 
       //如果没有指定密匙,可能标识用了主数据库的加密或没加密.

       if (!pKey || !nKeyLen)

       {
               if (!nDb)
              {
                      return  SQLITE_OK; //主数据库, 没有指定密钥所以没有加密.
              }
               else //附加数据库,使用主数据库的密钥.
              {
                     //获取主数据库的加密块并复制密钥给附加数据库使用

                     LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(

                                  sqlite3BtreePager(db->aDb[0].pBt));
 
                      if (!pBlock)
                            return  SQLITE_OK; //主数据库没有加密
 
                      if (!pBlock->ReadKey)
                            return  SQLITE_OK; //没有加密
 
                      memcpy(pBlock->ReadKey, &hKey, 16);
              }
       }
        else //用户提供了密码,从中创建密钥.
       {

              hKey = DeriveKey(pKey, nKeyLen);

       }
 
       //创建一个新的加密块,并将解码器指向新的附加数据库.
        if (hKey)
       {

              LPCryptBlock pBlock = CreateCryptBlock(hKey,

                           sqlite3BtreePager(db->aDb[nDb].pBt),  NULL);
 
              sqlite3pager_set_codec(sqlite3BtreePager(db->aDb[nDb].pBt),

                           sqlite3Codec, pBlock);

 
              rc = SQLITE_OK;
       }
 
        return rc;
}
 
// Changes the encryption key for an existing database.
int __stdcall  sqlite3_rekey_interop(sqlite3 *db,  const  void *pKey,  int nKeySize)
{

       Btree *pbt = db->aDb[0].pBt;

       Pager *p = sqlite3BtreePager(pbt);

       LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(p);

       unsigned char * hKey = DeriveKey(pKey, nKeySize);

        int rc =  SQLITE_ERROR;
 

       if (!pBlock && !hKey)

               return  SQLITE_OK;
 
       //重新加密一个数据库,改变pager的写密钥, 读密钥依旧保留.
        if (!pBlock) //加密一个未加密的数据库
       {

              pBlock = CreateCryptBlock(hKey, p, NULL);

              pBlock->ReadKey = 0; // 原始数据库未加密

              sqlite3pager_set_codec(sqlite3BtreePager(pbt), sqlite3Codec, pBlock);

       }
        else // 改变已加密数据库的写密钥
       {
              pBlock->WriteKey = hKey;
       }
 
       // 开始一个事务

       rc = sqlite3BtreeBeginTrans(pbt, 1);

        if (!rc)
       {
              // 用新密钥重写所有的页到数据库。

              Pgno nPage = sqlite3PagerPagecount(p);

              Pgno nSkip = PAGER_MJ_PGNO(p);

               void *pPage;

              Pgno n;

 

              for (n = 1; rc == SQLITE_OK && n <= nPage; n++)

              {
                      if (n == nSkip)
                            continue;
 
                     rc = sqlite3PagerGet(p, n, &pPage);
                      if (!rc)
                     {

                           rc = sqlite3PagerWrite(pPage);

                           sqlite3PagerUnref(pPage);
                     }
              }
       }
 
       // 如果成功,提交事务。
        if (!rc)
       {

              rc = sqlite3BtreeCommit(pbt);

       }
 
       // 如果失败,回滚。
        if (rc)
       {
              sqlite3BtreeRollback(pbt);
       }
 
       // 如果成功,销毁先前的读密钥。并使读密钥等于当前的写密钥。
        if (!rc)
       {
               if (pBlock->ReadKey)
              {
                     sqliteFree(pBlock->ReadKey);
              }
 
              pBlock->ReadKey = pBlock->WriteKey;
       }
        else // 如果失败,销毁当前的写密钥,并恢复为当前的读密钥。
       {
               if (pBlock->WriteKey)
              {
                     sqliteFree(pBlock->WriteKey);
              }
              pBlock->WriteKey = pBlock->ReadKey;
       }
 
       // 如果读密钥和写密钥皆为空,就不需要再对页进行编解码。
       // 销毁加密块并移除页的编解码器

       if (!pBlock->ReadKey && !pBlock->WriteKey)

       {

              sqlite3pager_set_codec(p, NULLNULL);

              DestroyCryptBlock(pBlock);
       }
 
        return rc;
}
 
/***
 下面是加密函数的主体
 ***/
int __stdcall  sqlite3_key_interop(sqlite3 *db,  const  void *pKey,  int nKeySize)
{

       return sqlite3CodecAttach(db, 0, pKey, nKeySize);

}
 
// 释放与一个页相关的加密块
void  sqlite3pager_free_codecarg( void *pArg)
{
        if (pArg)
              DestroyCryptBlock((LPCryptBlock) pArg);
}
 
#endif //# ifdef SQLITE_HAS_CODEC
 
特别说明:
DeriveKey 函数,这个函数是对密钥的扩展。比如,你要求密钥是128位,即是16字节,但是如果用户只输入 1个字节呢?2个字节呢?或输入50个字节呢?你得对密钥进行扩展,使之符合16字节的要求。

DeriveKey 函数就是做这个扩展的。有人把接收到的密钥求md5,这也是一个办法,因为md5运算结果固定16字节,不论你有多少字符,最后就是16字节。这是md5算法的特点。但是我不想用md5,因为还得为它添加包含一些 md5 的.c或.cpp文件。我不想这么做。我自己写了一个算法来扩展密钥,很简单的算法。当然,你也可以使用你的扩展方法,也而可以使用 md5 算法。只要修改 DeriveKey 函数就可以了。

在 DeriveKey 函数里,只管申请空间构造所需要的密钥,不需要释放,因为在另一个函数里有释放过程,而那个函数会在数据库关闭时被调用。参考上面 DeriveKey 函数来申请内存。






本文转自 tywali 51CTO博客,原文链接:http://blog.51cto.com/lancelot/940813,如需转载请自行联系原作者
目录
相关文章
|
7月前
|
算法 数据库 数据安全/隐私保护
rsa加密解密,使用rsa对密码加密
rsa加密解密,使用rsa对密码加密
|
6月前
|
存储 安全 数据库
对称加密的日常实践应用:以AES为例的加密解密指南
**摘要:** 本文介绍了对称加密算法AES在数据安全中的应用,强调了其在文件、通信和数据库加密中的重要性。通过Python示例展示了如何使用`cryptography`库实现AES-256的加密和解密,涉及密钥生成、CBC模式及PKCS7填充。同时,提醒注意密钥管理、模式选择和填充方式的选择对加密安全性的影响。
610 1
|
6月前
|
数据库 数据安全/隐私保护 数据库管理
QT中sqlite数据库数据加密/混淆---MD5/SHA1/SHA2/SHA3
QT中sqlite数据库数据加密/混淆---MD5/SHA1/SHA2/SHA3
|
5月前
|
算法 安全 数据安全/隐私保护
支付系统---微信支付09------数字签名,现在Bob想要给Pink写一封信,信件的内容不需要加密,怎样能够保证信息的完整性,使用信息完整性的主要手段是摘要算法,散列函数,哈希函数,H称为数据指纹
支付系统---微信支付09------数字签名,现在Bob想要给Pink写一封信,信件的内容不需要加密,怎样能够保证信息的完整性,使用信息完整性的主要手段是摘要算法,散列函数,哈希函数,H称为数据指纹
|
6月前
|
Java BI C#
技术笔记:SM4加密算法实现Java和C#相互加密解密
技术笔记:SM4加密算法实现Java和C#相互加密解密
106 0
|
7月前
|
Java 关系型数据库 MySQL
MySql数据库级别MD5加密java MD5加密解密工具包
MySql数据库级别MD5加密java MD5加密解密工具包
|
7月前
|
存储 算法 安全
加密解密(RSA)非对称加密算法
加密解密(RSA)非对称加密算法
|
7月前
|
数据库连接 API 数据库
SQLite3 数据库 C语言API 打开函数sqlite3_open 详解
SQLite3 数据库 C语言API 打开函数sqlite3_open 详解
319 0
|
7月前
|
PHP 数据安全/隐私保护
|
7月前
|
SQL 存储 关系型数据库
MySQL 时间日期函数,流程控制函数,加密解密函数以及聚合查询函数
MySQL 时间日期函数,流程控制函数,加密解密函数以及聚合查询函数
70 0