zoukankan      html  css  js  c++  java
  • Android SQLite 加密模块实现入门

    安卓的安全性那是众所周知,最近学习安卓apk反编译,发现某些即时通讯软件都封装了自己独立使用的数据库模块(从framework java/C++ 一直到底层的SQLite的C库),

    为了防止被root的手机抓取可能泄密的log,有些apk甚至独立封装了log库,安全意识不可谓不强。

    简要介绍一下SQLite,这是目前谷歌Android、苹果iOS、三星Tizen等移动设备系统默认的数据库,说它是世界上应用最广泛的的数据库一点也不夸张。

    SQLite官网:http://sqlite.org/index.html

    SQLite是最开源的代码,使用者可以随便修改,无需购买任何许可证,唯一遗憾的是,开源版本的不带加密模块,不幸中的万幸则是,它预留了接口,用户可以自己实现。

    当然,SQLite官方也有自己的需要购买许可证书加密模块提供:http://www.hwaci.com/sw/sqlite/see.html

    如果你不想花钱,或者你做的也是开源软件,那么第三方开源加密模块SQLCipher是个不错的选择:https://www.zetetic.net/sqlcipher/open-source/

    偶然看到一篇名为《某个sqlcrypto的算法概要》文章[http://www.fenlog.com/post/113.html],如获至宝,这个用来理解SQLite加密模块再简洁不过。

    简洁归简洁,对于一个急切看到“Hello World!”的人来说,文章中还要去找aes.h的相关实现,还是太麻烦,干脆就做一个最简单的字符串轮转的“加密”实现吧:

    #include "sqlite3.c" //Download SQLite 3.7.13 from http://olex.openlogic.com/packages/sqlite#package_detail_tabs
    //#include "aes.h"
     
    #define KEYLENGTH 16
    
    typedef struct _aes_ctx {
        int test;//Not used, just for test
    } aes_ctx;
    
    typedef struct _codec_ctx {
        char *pszPass;
        int nPassLen;
        aes_ctx m_ctxde;//not used
        aes_ctx m_ctxen;//not used
        Btree* m_bt; /* Pointer to B-tree used by DB */
        u8 *buff1;//back up pData
        u8 *buff2;//output buffer for encrypt data
     
    } codec_ctx;
    
    #define JIAMI
     
    void aes_decrypt(unsigned char in[], unsigned char out[], aes_ctx cx[1]) {
        int i;
        memcpy(out, in, 16);
    #ifdef JIAMI
        for(i=0; i<16; i++) {
            if(out[i] == 0) {
              out[i] == 255;
            } else {
              out[i]--;
            }
        }
    #endif
    }
    void aes_encrypt(unsigned char in[], unsigned char out[], aes_ctx cx[1]) {
        int i;
        memcpy(out, in, 16);
    #ifdef JIAMI
        for(i=0; i<16; i++) {
            if(out[i] == 255) {
              out[i] == 0;
            } else {
              out[i]++;
            }
        }
    #endif
    }
    
    void aes_decrypt_key128(const unsigned char *zKey, aes_ctx cx[1]) {
        /* not used */
    }
    
    void aes_encrypt_key128(const unsigned char *zKey, aes_ctx cx[1]) {
        /* not used */
    }
    
    void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
        codec_ctx *ctx = (codec_ctx *) iCtx;
        unsigned char *pData = (unsigned char *) data;
        int pageSize = sqlite3BtreeGetPageSize(ctx->m_bt);
        int nBlock = pageSize / 16;
        int i;
        unsigned char szTmp[16];
        unsigned char *out=NULL;
        ctx->buff1 = sqlite3_malloc(pageSize+4);
        ctx->buff2 = sqlite3_malloc(pageSize+4);
        if(ctx->buff1==NULL || ctx->buff2==NULL) {
            printf("sqlite3_malloc err!
    ");
            return pData;
        }
        memcpy(ctx->buff1, data, pageSize);
        memcpy(ctx->buff2, data, pageSize);
        printf("Pgno:%4d, mode:%2d, Data:%8d
    ", pgno, mode, pData[0]);
        switch(mode) {
        case 0: /* Decrypt */
        case 2:
        case 3:
            for (i = 0; i < nBlock; i++)
            {
                aes_decrypt(&pData[i * 16], szTmp, &ctx->m_ctxde);
                memcpy(&pData[i * 16], szTmp, 16);
            }
            out = pData;
            break;
     
        case 6: /* Encrypt */
            for (i = 0; i < nBlock; i++)
            {
                aes_encrypt(&pData[i * 16], szTmp, &ctx->m_ctxen);
                //memcpy(&pData[i * 16], szTmp, 16);
                memcpy(&ctx->buff2[i * 16], szTmp, 16);
            }
            out = ctx->buff2;
            break;
     
        case 7: /* Encrypt a page for the journal file */
            for (i = 0; i < nBlock; i++)
            {
                aes_encrypt(&pData[i * 16], szTmp, &ctx->m_ctxde);
                //memcpy(&pData[i * 16], szTmp, 16);
                memcpy(&ctx->buff2[i * 16], szTmp, 16);
            }
            out = ctx->buff2;
            break;
        }
        if(pgno == 1) { // Fisrt data page, offest 16 is page size, needed to init the DB setting.
            for (i = 16; i < 23; i++) {
                out[i] = ctx->buff1[i];
            }
        }
        //return data;
        return out;
    }
     
     
    void sqlite3FreeCodecArg(void *pCodecArg) {
        codec_ctx *ctx = (codec_ctx *)pCodecArg;
        if(pCodecArg == NULL) 
            return;
     
        sqlite3_free(ctx->pszPass);
        memset(ctx, 0, sizeof(codec_ctx));
        sqlite3_free(ctx);
    }
     
     
    int sqlite3CodecAttach(sqlite3* db, int nDb, const void* zKey, int nKey) {
        struct Db *pDb = &db->aDb[nDb];
     
        if(nKey && zKey && pDb->pBt) {
            codec_ctx *ctx = sqlite3Malloc(sizeof(codec_ctx));
    
            aes_decrypt_key128((const unsigned char *)zKey, &ctx->m_ctxde);
            aes_encrypt_key128((const unsigned char *)zKey, &ctx->m_ctxen);
    
            ctx->m_bt = pDb->pBt; /* assign pointer to database btree structure */
            ctx->pszPass = (char *)sqlite3Malloc(nKey + 1);
            memcpy(ctx->pszPass, zKey, nKey);
            ctx->pszPass[nKey] = '';
            ctx->nPassLen = nKey;
    
            sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx);
        }
        return SQLITE_OK;
    }
     
     
    void sqlite3pager_get_codec(Pager *pPager, void **ctx) {
        *ctx = pPager->pCodec;
    }
     
     
    void sqlite3CodecGetKey(sqlite3* db, int nDb, void** zKey, int* nKey) {
        struct Db *pDb = &db->aDb[nDb];
    
        if( pDb->pBt ) {
            codec_ctx *ctx;
            sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
    
            if(ctx) {
                *zKey = ctx->pszPass;
                *nKey = ctx->nPassLen;
            } 
            else {
                *zKey = NULL;
                *nKey = 0;
            }
        }
    }
     
    void sqlite3_activate_see(const char *info) {
        //ignore
    }
     
    int sqlite3_rekey(sqlite3 *db, const void *zKey, int nKey) {
        //ignore
        return SQLITE_ERROR;
    }
     
    int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) {
        /* The key is only set for the main database, not the temp database  */
        return sqlite3CodecAttach(db, 0, zKey, nKey);
    }
    #define checkErr(wif,rc1,rc2) do{if(wif){printf("Error:%d,    Func:%s,    Line:%d
    ", rc1, __func__, __LINE__);goto error;}}while(0)
    int main() {
        int rc;
        char *zErr = 0;
        sqlite3 *pdb = NULL;
        sqlite3_stmt *pStmt = NULL;
        printf("------------------------- Open DB -----------------------------
    ");
        rc = sqlite3_open("test.db",&pdb);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        printf("-------------------------Set DB Key----------------------------
    ");
        rc = sqlite3_key(pdb, "123456", 6);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        printf("------------------------- Write DB -----------------------------
    ");
        rc = sqlite3_exec(pdb, "drop table if exists test;", 0, 0, &zErr);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        rc = sqlite3_exec(pdb, "create table test(id,name);", 0, 0, &zErr);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        rc = sqlite3_exec(pdb, "insert into test values(123,'Hello Just DB!');", 0, 0, &zErr);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        printf("------------------------- Read DB -----------------------------
    ");
        //Select from test
        rc = sqlite3_prepare(pdb, "select name from test;", -1, &pStmt, 0);
        checkErr(rc != SQLITE_OK, rc, rc);
        if(pStmt) {
            rc = sqlite3_step(pStmt);
            //nResult = sqlite3_column_count(pStmt);
            while( rc==SQLITE_ROW ) {
                printf("Name: %s
    ",sqlite3_column_text(pStmt, 0));
                rc = sqlite3_step(pStmt);
            }
        }
        rc = sqlite3_finalize(pStmt);
        checkErr(rc != SQLITE_OK, rc, rc);
    
        sqlite3_close(pdb);
        checkErr(rc != SQLITE_OK, rc, rc);
    error:
        printf("RC = %d
    ",rc);
        return 0;
    }

    SQLite官方的版本太新了,这个实现要在比较旧的版本(可以从 http://olex.openlogic.com/packages/sqlite#package_detail_tabs 下载历史版本)上实现。

    史上“最简单”的SQLite“加密”模块就这样实现了:

    备注:aHR0cCUzQS8vd3d3LmNuYmxvZ3MuY29tL3poaGQv

     

  • 相关阅读:
    单例模式中的懒汉式以及线程安全性问题
    单例模式中的饿汉模式
    自我管理的8个好习惯
    从java字节码角度看线程安全性问题
    工作上的建议
    从线程的优先级看饥饿问题
    多线程存在哪些风险
    DirectX SDK (June 2010)安装错误S1023,解决方法
    Microsoft DirectX SDK 2010 版本下载
    如果程序集是从 Web 上下载的,即使它存储于本地计算机,Windows 也会将其标记为 Web 文件,http://go.microsoft.com/fwlink/?LinkId=179545
  • 原文地址:https://www.cnblogs.com/zhhd/p/5410611.html
Copyright © 2011-2022 走看看