网上有个3.3.7的xxtea加密,但3.61起,内部有代码变化了

解决方案 »

  1.   

    我正在用sqlite,现在也是为加密的事情头疼,网上.net版本的sqlite3.dll已经实现了加密,我在xp下测试可以通过,2000下不行,98就更不可以了,因为这个dll使用了windows自带的加密API.
      

  2.   

    xxtea的改到新版本的加密不难啊
    以下代码为修改后的xxtea,适用最新的sqlite 3个文件源码的版本#define DATA_TO_PGHDR(D)  (&((PgHdr*)(D))[-1])
    #include "sqlite3.c"
    #ifndef SQLITE_OMIT_DISKIO
    #ifdef SQLITE_HAS_CODEC
    #include "md5.h"#define CRYPT_OFFSET 8
    #define BYTE unsigned chartypedef struct _CryptBlock
    {
        BYTE*     ReadKey;     // 读数据库和写入事务的密钥
        BYTE*     WriteKey;    // 写入数据库的密钥
        int       PageSize;    // 页的大小
        BYTE*     Data;
    } CryptBlock, *LPCryptBlock;int xxtea( int * v, int n , int * k ) {
        unsigned int z/*=v[n-1]*/, y=v[0], sum=0,  e,    DELTA=0x9e3779b9 ;
        int m, p, q ;
        if ( n>1) {
            /* Coding Part */
            z = v[n-1];
            q = 6+52/n ;
            while ( q-- > 0 ) {
                sum += DELTA ;
                e = sum >> 2&3 ;
                for ( p = 0 ; p < n-1 ; p++ ){
                    y = v[p+1],
                        z = v[p] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);
                }
                y = v[0] ;
                z = v[n-1] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);
            }
            return 0 ;        /* Decoding Part */
        }else if ( n <-1 ) {
            n = -n ;
            q = 6+52/n ;
            sum = q*DELTA ;
            while (sum != 0) {
                e = sum>>2 & 3 ;
                for (p = n-1 ; p > 0 ; p-- ){
                    z = v[p-1],
                        y = v[p] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);
                }
                z = v[n-1] ;
                y = v[0] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);
                sum -= DELTA ; 
            }
            return 0 ;
        }    return 1 ;
    } /* Signal n=0,1,-1 */
    void * sqlite3pager_get_codecarg(Pager *pPager)
    {
        return (pPager->xCodec) ? pPager->pCodecArg: NULL;
    }void sqlite3_activate_see(const char *info)
    {
    }//创建或更新一个页的加密算法索引.此函数会申请缓冲区.
    static LPCryptBlock CreateCryptBlock(BYTE* hKey, Pager *pager, LPCryptBlock pExisting)
    {
        LPCryptBlock pBlock;    if (!pExisting) //创建新加密块
        {
            pBlock = sqlite3_malloc(sizeof(CryptBlock));
            ZeroMemory(pBlock, sizeof(CryptBlock));
            pBlock->ReadKey = hKey;
            pBlock->WriteKey = hKey;
            pBlock->PageSize = pager->pageSize;
            pBlock->Data = (BYTE*)sqlite3_malloc(pBlock->PageSize + CRYPT_OFFSET);
        }
        else //更新存在的加密块
        {
            pBlock = pExisting;
            if ( pBlock->PageSize != pager->pageSize && !pBlock->Data){
                sqlite3_free(pBlock->Data);
                pBlock->PageSize = pager->pageSize;
                pBlock->Data = (BYTE*)sqlite3_malloc(pBlock->PageSize + CRYPT_OFFSET);
            }
        }
        ZeroMemory(pBlock->Data, pBlock->PageSize + CRYPT_OFFSET);
        return pBlock;
    }//销毁一个加密块及相关的缓冲区,密钥.
    static void DestroyCryptBlock(LPCryptBlock pBlock)
    {
        //销毁读密钥.
        if (pBlock->ReadKey){
            sqlite3_free(pBlock->ReadKey);
        }    //如果写密钥存在并且不等于读密钥,也销毁.
        if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey){
            sqlite3_free(pBlock->WriteKey);
        }    if(pBlock->Data){
            sqlite3_free(pBlock->Data);
        }    //释放加密块.
        sqlite3_free(pBlock);
    }//加密/解密函数, 被pager调用
    void * sqlite3Codec(void *pArg, BYTE *data, Pgno nPageNum, int nMode)
    {
        LPCryptBlock pBlock = (LPCryptBlock)pArg;
        int len = 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;
            len = 0 - (pBlock->PageSize/4);
            xxtea(data, len, pBlock->ReadKey);        break;
        case 6: //加密一个主数据库文件的页
            if (!pBlock->WriteKey) break;        CopyMemory(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
            data = pBlock->Data + CRYPT_OFFSET;        len = pBlock->PageSize/4;
            xxtea(data , len, pBlock->WriteKey);
            break;
        case 7: //加密事务文件的页
            /*在正常环境下, 读密钥和写密钥相同. 当数据库是被重新加密的,读密钥和写密钥未必相同.
            回滚事务必要用数据库文件的原始密钥写入.因此,当一次回滚被写入,总是用数据库的读密钥,
            这是为了保证与读取原始数据的密钥相同.
            */
            if (!pBlock->ReadKey) break;        CopyMemory(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
            data = pBlock->Data + CRYPT_OFFSET;
            len = pBlock->PageSize/4;
            xxtea(data, len, pBlock->ReadKey);
            break;
        }    return data;
    }// 从用户提供的缓冲区中得到一个加密密钥
    static BYTE * DeriveKey(const void *pKey, int nKeyLen)
    {
        BYTE *  hKey = NULL;
        BYTE *  digest = NULL;
        MD5_CTX md5ctx;    if ((pKey == NULL) || (nKeyLen <1 ))
        {
            return NULL;
        }    digest = sqlite3_malloc(16);
        MD5Init(&md5ctx);
        MD5Update(&md5ctx, (unsigned char*)pKey, nKeyLen);
        MD5Final(digest, &md5ctx);    hKey = digest;
        return hKey;
    }
      

  3.   


    //被sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.
    int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen)
    {
        int rc = SQLITE_ERROR;
        BYTE* 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);
            sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), sqlite3Codec, pBlock);
            db->aDb[nDb].pAux = pBlock;
            db->aDb[nDb].xFreeAux = DestroyCryptBlock;        rc = SQLITE_OK;
        }
        return rc;
    }// 释放与一个页相关的加密块
    void sqlite3pager_free_codecarg(void *pArg)
    {
        if (pArg)
            DestroyCryptBlock((LPCryptBlock)pArg);
    }//不保存用户的原始密码.返回NULL.
    void sqlite3CodecGetKey(sqlite3 *db, int nDb, void **ppKey, int *pnKeyLen)
    {
        *ppKey = NULL;
        *pnKeyLen = 0;
    }//密钥并不保留到临时空间,仅保存于主数据库.
    int sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize)
    {
        return sqlite3CodecAttach(db, 0, pKey, nKeySize);
    }//改变已有数据库的加密密钥
    int 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);
        BYTE * 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; // 原始数据库未加密
            sqlite3PagerSetCodec(sqlite3BtreePager(pbt), sqlite3Codec, pBlock);
            db->aDb[0].pAux = pBlock;
            db->aDb[0].xFreeAux = DestroyCryptBlock;
        }
        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)
            {
                sqlite3_free(pBlock->ReadKey);
            }
            pBlock->ReadKey = pBlock->WriteKey;
        }
        else// 如果失败,销毁当前的写密钥,并恢复为当前的读密钥。
        {
            if (pBlock->WriteKey)
            {
                sqlite3_free(pBlock->WriteKey);
            }
            pBlock->WriteKey = pBlock->ReadKey;
        }    // 如果读密钥和写密钥皆为空,就不需要再对页进行编解码。
        // 销毁加密块并移除页的编解码器
        if (!pBlock->ReadKey && !pBlock->WriteKey)
        {
            sqlite3PagerSetCodec(p, NULL, NULL);
            db->aDb[0].pAux = NULL;
            db->aDb[0].xFreeAux = NULL;
            DestroyCryptBlock(pBlock);
        }    return rc;
    }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);
    }#endif // SQLITE_HAS_CODEC
    #endif // SQLITE_OMIT_DISKIO
      

  4.   

    不知你是用什么连 SQLite 
    Delphi 有个免费的控件 ASQSQLite ,在源代码中自己实现 SQLite3_Key 和 SQLite3_ReKey ,只要加很少几行代码就可以支持加密了ODBC驱动也有
      

  5.   

    我把这些方法移植到3.7.13的源码中发现这个不能加密,原来能看到的数据加密后任然可见.后面发现xxtea这个算法根本就没被调用,即使赋值给sqlite3中pager->xCodec后也没见做任何操作,请问这是为什么不能加密。是不是这组代码不完善啊?还有就是如果我需要在linux下编译添加接口后的sqlite3如何编译呢?求大侠尽快回复!!