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[]) {
int i;
memcpy(out, in, );
#ifdef JIAMI
for(i=; i<; i++) {
if(out[i] == ) {
out[i] == ;
} else {
out[i]--;
}
}
#endif
}
void aes_encrypt(unsigned char in[], unsigned char out[], aes_ctx cx[]) {
int i;
memcpy(out, in, );
#ifdef JIAMI
for(i=; i<; i++) {
if(out[i] == ) {
out[i] == ;
} else {
out[i]++;
}
}
#endif
} void aes_decrypt_key128(const unsigned char *zKey, aes_ctx cx[]) {
/* not used */
} void aes_encrypt_key128(const unsigned char *zKey, aes_ctx cx[]) {
/* 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 / ;
int i;
unsigned char szTmp[];
unsigned char *out=NULL;
ctx->buff1 = sqlite3_malloc(pageSize+);
ctx->buff2 = sqlite3_malloc(pageSize+);
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[]);
switch(mode) {
case : /* Decrypt */
case :
case :
for (i = ; i < nBlock; i++)
{
aes_decrypt(&pData[i * ], szTmp, &ctx->m_ctxde);
memcpy(&pData[i * ], szTmp, );
}
out = pData;
break; case : /* Encrypt */
for (i = ; i < nBlock; i++)
{
aes_encrypt(&pData[i * ], szTmp, &ctx->m_ctxen);
//memcpy(&pData[i * 16], szTmp, 16);
memcpy(&ctx->buff2[i * ], szTmp, );
}
out = ctx->buff2;
break; case : /* Encrypt a page for the journal file */
for (i = ; i < nBlock; i++)
{
aes_encrypt(&pData[i * ], szTmp, &ctx->m_ctxde);
//memcpy(&pData[i * 16], szTmp, 16);
memcpy(&ctx->buff2[i * ], szTmp, );
}
out = ctx->buff2;
break;
}
if(pgno == ) { // Fisrt data page, offest 16 is page size, needed to init the DB setting.
for (i = ; i < ; 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, , 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 + );
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 = ;
}
}
} 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, , 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 = ;
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, "", );
checkErr(rc != SQLITE_OK, rc, rc); printf("------------------------- Write DB -----------------------------\n");
rc = sqlite3_exec(pdb, "drop table if exists test;", , , &zErr);
checkErr(rc != SQLITE_OK, rc, rc); rc = sqlite3_exec(pdb, "create table test(id,name);", , , &zErr);
checkErr(rc != SQLITE_OK, rc, rc); rc = sqlite3_exec(pdb, "insert into test values(123,'Hello Just DB!');", , , &zErr);
checkErr(rc != SQLITE_OK, rc, rc); printf("------------------------- Read DB -----------------------------\n");
//Select from test
rc = sqlite3_prepare(pdb, "select name from test;", -, &pStmt, );
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, ));
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 ;
}
SQLite官方的版本太新了,这个实现要在比较旧的版本(可以从 http://olex.openlogic.com/packages/sqlite#package_detail_tabs 下载历史版本)上实现。
史上“最简单”的SQLite“加密”模块就这样实现了:
备注:aHR0cCUzQS8vd3d3LmNuYmxvZ3MuY29tL3poaGQv
Android SQLite 加密模块实现入门的更多相关文章
- Android SQLite 通配符查询找不到参数问题
使用Android SQLite中SQLiteDatabase类的query方法查询时,如果where中包含通配符,则参数会无法设置,如类似下面的方法查询时 SQLiteDatabase db = d ...
- Android+Sqlite 实现古诗阅读应用(三)
往期传送门: Android+Sqlite 实现古诗阅读应用(一) Android+Sqlite 实现古诗阅读应用(二) 加入截图分享的功能. 很多应用都有分享的功能,我也想在我的古诗App里加入这个 ...
- Android+Sqlite 实现古诗阅读应用(二)
传送门:Android+Sqlite 实现古诗阅读应用(一) Hi,又回来了,最近接到很多热情洋溢的小伙伴们的来信,吼开心哈,我会继续努力的=-=! 上回的东西我们做到了有个textview能随机选择 ...
- Android Sqlite 数据库版本更新
Android Sqlite 数据库版本更新 http://87426628.blog.163.com/blog/static/6069361820131069485844/ 1.自己写一个类继承 ...
- Android SQLite总结(一) (转)
Android SQLite总结(一) 郑海波 2012-08-21 转载请声明:http://blog.csdn.net/nuptboyzhb/article/details/7891887 前言 ...
- android SQLite使用SQLiteOpenHelper类对数据库进行操作
android SQLite使用SQLiteOpenHelper类对数据库进行操作 原文: http://byandby.iteye.com/blog/835580
- Android sqlite管理数据库基本用法
Android操作系统中内置了sqlite数据库(有关sqlite数据库详细介绍见:http://zh.wikipedia.org/wiki/SQLite),而sqllite本身是一个很小型的数据库, ...
- Android SQLite 数据库详细介绍
Android SQLite 数据库详细介绍 我们在编写数据库应用软件时,需要考虑这样的问题:因为我们开发的软件可能会安装在很多用户的手机上,如果应用使用到了SQLite数据库,我们必须在用户初次使用 ...
- Android Sqlite 导入CSV文件 .
http://blog.csdn.net/johnnycode/article/details/7413111 今天遇到 Oracle 导出的12万条CSV格式数据导入 Android Sqlite ...
随机推荐
- 2015年4月27日---C语言:输出特殊图案,请在c环境中运行,看一看,Very Beautiful!
---恢复内容开始--- 题目:输出特殊图案,请在c环境中运行,看一看,Very Beautiful! 1.程序分析:字符共有256个.不同字符,图形不一样. 2.程序源代码: [code=c] #i ...
- php 创建删除数据库
<?php $dbhost = 'localhost:3306'; $dbuser = 'root'; $dbpass = ''; $conn = mysql_connect($dbhost, ...
- android 定时器总结
1:handler实现定时器的功能 Handler handler=new Handler(); //立即执行Runnable对象 public final boolean post(Runnab ...
- PD生成oracle表名带引号解决方案
使用PowerDesigner生成数据库建表SQL脚本时,尤其是Oracle数据库时,表名一般会带引号.其实加引号是PL/SQL的规范,数据库会 严格按照“”中的名称建表,如果没有“”,会按照ORAC ...
- C#实现http断点下载
我们寄希望于万能的解决方案,但是现实的情况总是很糟糕.在软件编程的世界中,技术分散的情况尤为严重,且不说各种语言拥有的优势不能融合,单就一门语言而言,就拥有众多的技术和相关技术需要学习.网络编程就是这 ...
- 设计模式之建造者模式(Builder)
一个人活到70岁以上,都会经历这样的几个阶段:婴儿,少年,青年,中年,老年.并且每个人在各个阶段肯定是不一样的呀,我觉得可以说世界上不存在两个人在人生的这5个阶段的生活完全一样,但是活到70岁以上的人 ...
- Oracle\PLSQL Developer报“动态执行表不可访问,本会话的自动统计被禁止”的解决方案
现象: 第一次用PLSQL Developer连接数据库,若用sys用户登录并操作则正常,若用普通用户比如haishu登录并创建一个表则报错“动态执行表不可访问,本会话的自动统计被禁止.在执行菜单里你 ...
- USACO Section 1.3 Mixing Milk 解题报告
题目 题目描述 Merry Milk Makers 公司的业务是销售牛奶.它从农夫那里收购N单位的牛奶,然后销售出去.现在有M个农夫,每个农夫都存有一定量的牛奶,而且每个农夫都会有自己的定价.假设所有 ...
- STM32的优先级NVIC_PriorityGroupConfig的理解及其使用(转)
源:http://blog.csdn.net/yx_l128125/article/details/9703843 写作原由:因为之前有对stm32 优先级做过研究,但是没时间把整理的东西发表,最近项 ...
- ural1553 Caves and Tunnels
Caves and Tunnels Time limit: 3.0 secondMemory limit: 64 MB After landing on Mars surface, scientist ...