安卓的安全性那是众所周知,最近学习安卓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
原文:http://www.cnblogs.com/zhhd/p/5410611.html