首页 > 移动平台 > 详细

Android SQLite 加密模块实现入门

时间:2016-04-20 00:18:53      阅读:378      评论:0      收藏:0      [点我收藏+]

安卓的安全性那是众所周知,最近学习安卓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!\n");
        return pData;
    }
    memcpy(ctx->buff1, data, pageSize);
    memcpy(ctx->buff2, data, pageSize);
    printf("Pgno:%4d, mode:%2d, Data:%8d\n", 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] = \0;
        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\n", rc1, __func__, __LINE__);goto error;}}while(0)
int main() {
    int rc;
    char *zErr = 0;
    sqlite3 *pdb = NULL;
    sqlite3_stmt *pStmt = NULL;
    printf("------------------------- Open DB -----------------------------\n");
    rc = sqlite3_open("test.db",&pdb);
    checkErr(rc != SQLITE_OK, rc, rc);

    printf("-------------------------Set DB Key----------------------------\n");
    rc = sqlite3_key(pdb, "123456", 6);
    checkErr(rc != SQLITE_OK, rc, rc);

    printf("------------------------- Write DB -----------------------------\n");
    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 -----------------------------\n");
    //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\n",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\n",rc);
    return 0;
}

 

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

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

技术分享

 

备注:aHR0cCUzQS8vd3d3LmNuYmxvZ3MuY29tL3poaGQv

 

 

 

Android SQLite 加密模块实现入门

原文:http://www.cnblogs.com/zhhd/p/5410611.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!