SQLite 支持的数据类型

sqlite 支持 5 种数据类型

  • null,当某个项未被赋值时,类型为空;赋值后,类型即为建表时为该列指定的类型了。
    但由于SQLite采用的是动态数据类型,会根据存入值自动判断,因此在创建表的时候,字段类型是允许设为null的。但是在首次存入值之后,会根据该值修改实际类型。
  • integer,整型(布尔值会被整型,true->1, false->0)
  • float,浮点型
  • string,字符串类型
  • blob,binary large object,是用来存储二进制的大的对象的字段类型(实际中很少用到)

为什么要加密数据库

对于 Root 过的安装手机,可以随意访问 /data/data/<package_name>/databases 目录下的任意文件,在这里就可以查看到数据库中存储的所有数据。对于一般数据没啥问题,但如果涉及到一些账号密码,或者是聊天内容的时候,程序显然就面临着严重的安全漏洞和隐患了,因此这时候,对数据库进行加密就显得尤为重要。

sqlcipher

官网

greendao 支持数据库加密官网:http://greenrobot.org/greendao/documentation/database-encryption/

SQLCipher 官网:https://www.zetetic.net/sqlcipher/

sqlcipher的特性

  • 开源且继承自 sqlite
  • 透明,使用 256位 AES 加密
  • 防篡改设计
  • 跨平台且零配置

依赖

在 Android 上添加 SQLCipher 依赖:https://www.zetetic.net/sqlcipher/sqlcipher-for-android/

在 build.gradle 中的 dependencies 里添加

dependencies {//数据库加密implementation 'net.zetetic:android-database-sqlcipher:4.2.0'
}

使用方式

sqlcipher 在使用上和 android 自带的数据库并没有什么区别,唯一的区别就是打开的时候,需要提供密码。如果密码传 null,则等价于数据库不加密

引用 sqlcipher 类

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteException;

打开数据库

sqlcipher 是对整个数据库进行加密,open时进行解密,所以应尽量减少打开的次数。

/**
* 初始化并打开数据库* @param context 上下文* @param dbName 数据库名称(不用加后缀)* @param password 加密数据库密码(密码为null表示不加密)*/
public void open(Context context, String dbName, String password) {this.context = context;this.dbName = dbName + ".db";// 数据库会存放在 /data/data/<包名>/databases/ 目录下面File dbFile = context.getDatabasePath(this.dbName);File dir = dbFile.getParentFile();//如果目录不存在,则递归创建if (!dir.exists()) dir.mkdirs();//加载用于加密的so库SQLiteDatabase.loadLibs(context);//打开或者创建数据库,通过密码对数据库进行解密,如果password为null,则等价于原生的未加密的数据库db = SQLiteDatabase.openOrCreateDatabase(dbFile, password, null);
}

关闭数据库

    /*** 关闭数据库*/public void close() {//仅在数据库已创建,且已打开的情况下,关闭数据库if(db != null && db.isOpen()) db.close();}

执行SQL语句

/*** 执行脚本(仅在error非空的时候,返回结果才有效)** @param sql 待执行的SQL脚本* @return 执行成功返回true,否则返回false*/
public boolean execSQL(String sql) {error = null;try {//如果有多条相关的语句同时执行,则最好加上事务,以便出错时能回滚到初始状态// db.beginTransaction();db.execSQL(sql);// db.endTransaction();return true;} catch (SQLiteException e) {e.printStackTrace();error = e.toString();return false;}
}

查询

/**
* 查询数据,{"colName":[],"type":[],"value":[[],[]]}}(仅在error非空的时候,返回结果才有效)*/
public JSONObject queryToJson(String sql) {error = null;Cursor cursor = null;JSONObject jsonObject = new JSONObject();try {cursor = db.rawQuery(sql, null);//判断游标是否为空//cursor的初始位置是从下标为-1的地方开始,一定要先moveFirst才能使用if (cursor != null && cursor.moveToFirst()) {int columnCount = cursor.getColumnCount();if (columnCount > 0) {//列名JSONArray colNames = new JSONArray();//每一列的类型JSONArray typeNames = new JSONArray();//列的值JSONArray values = new JSONArray();for (int i = 0; i < columnCount; i++) {int type = cursor.getType(i);String name = cursor.getColumnName(i);colNames.put(name);switch (type) {case Cursor.FIELD_TYPE_NULL://通常情况下列的类型不为空。但由于SQLite采用的是动态数据类型,会根据存入值自动判断,因此在创建表的时候,字段类型是允许设为null的typeNames.put("NULL");break;case Cursor.FIELD_TYPE_INTEGER:typeNames.put("INTEGER");break;case Cursor.FIELD_TYPE_FLOAT:typeNames.put("FLOAT");break;case Cursor.FIELD_TYPE_STRING:typeNames.put("STRING");break;case Cursor.FIELD_TYPE_BLOB:typeNames.put("BLOB");break;}}for (; !cursor.isAfterLast(); cursor.moveToNext()) {JSONArray array = new JSONArray();for (int i = 0; i < columnCount; i++) {int type = cursor.getType(i);try {switch (type) {case Cursor.FIELD_TYPE_NULL://当值为空时,走的是这里array.put(null);break;case Cursor.FIELD_TYPE_INTEGER:array.put(cursor.getInt(i));break;case Cursor.FIELD_TYPE_FLOAT:array.put(cursor.getFloat(i));break;case Cursor.FIELD_TYPE_STRING:array.put(cursor.getString(i));break;case Cursor.FIELD_TYPE_BLOB://json不能存byte[],因此只能用bcd编码或base64编码来存array.put(BcdUtil.toString(cursor.getBlob(i)));break;}} catch (JSONException e) {e.printStackTrace();}}values.put(array);}jsonObject.put("colName", colNames);jsonObject.put("type", typeNames);jsonObject.put("value", values);} else {// 表的列为空error = context.getString(R.string.table_column_empty);}} else {//获取游标失败error = context.getString(R.string.get_cursor_fail);}} catch (SQLiteException e) {e.printStackTrace();error = e.getMessage();} catch (JSONException e) {e.printStackTrace();error = e.getMessage();} finally {if (cursor != null) {cursor.close();}}return jsonObject;
}

测试语句

public void testDatabase() {String createTable = "CREATE TABLE IF NOT EXISTS person ( id integer primary key autoincrement, name varchar(20), age integer, other null )";String insertInto = "INSERT INTO person ( name, age, other ) VALUES ( '%s', %d, %s )";String insertInto2 = "INSERT INTO person ( name, age, other ) VALUES ( 'bird', NULL, NULL )";String insertInto3 = "INSERT INTO person ( name, age, other ) VALUES ( NULL, 5, NULL )";String selectFrom = "SELECT * FROM person";//sqlite 不支持 drop column,会抛出异常String alertTable = "ALTER TABLE person DROP COLUMN age";String updateItem = "UPDATE person set age = 99 where upper(name) = upper('bob')";String dropTable = "DROP TABLE IF EXISTS person";String[] names = {"Tina", "Bob", "Tom"};int[] ages = {18, 30, 24};Object[] others = {"'other'", 11, true};dialog = DialogUtil.show(activity, data, "", null);new Thread(()->{//执行结果boolean sqlResult;JSONObject sqlSelect = null;sb = new StringBuilder();SQLiteHelper sqLiteHelper = SQLiteHelper.getInstance(activity, "test", null);sqLiteHelper.execSQL(createTable);sqlResult = sqLiteHelper.execSQL(createTable);StringUtil.appendLine(sb, "创建表:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "创建后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());for (int i = 0; i < names.length; i++) {sqlResult = sqLiteHelper.execSQL(String.format(insertInto, names[i], ages[i], others[i]));}StringUtil.appendLine(sb, "插入数据:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "插入后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());sqlResult = sqLiteHelper.execSQL(insertInto2);StringUtil.appendLine(sb, "插入name=NULL的数据:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "插入name=NULL后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());sqlResult = sqLiteHelper.execSQL(insertInto3);StringUtil.appendLine(sb, "插入age=NULL的数据:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "插入age=NULL后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());sqlResult = sqLiteHelper.execSQL(updateItem);StringUtil.appendLine(sb, "更新数据:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "跟新数据后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());sqlResult = sqLiteHelper.execSQL(alertTable);StringUtil.appendLine(sb, "修改表结构:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "修改表结构后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());sqlResult = sqLiteHelper.execSQL(dropTable);StringUtil.appendLine(sb, "删除表:" + sqlResult);setDialogMessage(activity, sb.toString());sqlSelect = sqLiteHelper.queryToJson(selectFrom);StringUtil.appendLine(sb, "删除表后的结果:" + sqlSelect);setDialogMessage(activity, sb.toString());}).start();
}

测试结果

从测试结果中可以看出几点

  • sqlite 不支持 drop
  • 如果值为空,则值的类型为 null
  • 创建表时,可以设置列的类型为null,sqlite会根据第一个存进去的值动态修改列的类型
  • sqlite允许存储和类类型不一致的值

将代码片段 Object[] others = {"'other'", 11, true}; 替换为 Object[] others = { 11, "'other'", true}; 后,卸载程序,然后再次安装运行可以发现,类型从 string 变为 integer,这说明,如果在创建表的时候将类型设为 null,那么它会根据第一个存进去的值,来修改列的类型。但不会改变 .schema 命令打印出的创建表的语句,对于存入的非列类型的值也不会报错。

查看数据库

https://blog.csdn.net/chy555chy/article/details/101511609

Android 使用 sqlcipher 加密数据库相关推荐

  1. Android studio+SQLCipher加密SQLite数据库的几个坑

    Android studio SQLCipher加密SQLite数据库的几个坑 前言:Android 自带的SQLite数据库可以用于数据存储,但是,大家都知道其数据是存储于手机的data/data/ ...

  2. 【逆向】【Android微信】加密数据库踩坑

    2020-02-19 晴 焦作 出场人物 饭咸 - 程序员 沉瓶 - 产品经理 工作环境 硬件:MacBook Pro (Retina, 13-inch, Early 2015) 硬件相应系统:mac ...

  3. 独家食用指南系列|Android端SQLCipher的攻与防新编

    大家好,今天给大家的是本周技术拆解官的第二篇文章,主题依然是沿用上一篇文章的主题–Android端SQLite的"食用指南",上篇文章我们讲到了基本的SQLite的定义.使用方法以 ...

  4. android sqlcipher 加密,Android 数据库加密 SQLCipher使用方法

    android sqlcipher使用方法 最近在做数据库加密,遇到了些问题,特此记录 greendao 支持数据库加密 网址https://greenrobot.org/greendao/docum ...

  5. 利用Android手机破解微信加密数据库EnMicroMsg.db文件

    要先批评一下微信,居然用开源的数据库加密方式,这不是一破解一个准吗? 如果你的模拟器或者真机已经获得了root权限,就可以直接将记录聊天记录的数据库文件拷贝出来,数据库文件具体存放位置如下: 在/da ...

  6. 直接利用Android手机破解微信加密数据库EnMicroMsg.db

    ※首先,简单介绍一下微信加密数据库EnMicroMsg.db的破解方法: 要先批评一下微信,居然用开源的数据库加密方式,这不是一破解一个准吗... 如果你的模拟器或者真机已经获得了root权限,就可以 ...

  7. android sqlite加密数据库,Android Sqlite数据库加密

    Android使用的是开源的SQLite数据库,数据库本身没有加密,加密思路通常有两个: 1. 对几个关键的字段使用加密算法,再存入数据库 2. 对整个数据库进行加密 SQLite数据库加密工具: 收 ...

  8. android sqlite解密,SQlite数据库的加密与解密

    一 关于SQlite Android系统自带的SQlite是明文存储,不支持加密 二 SQlite加密方式 内容加密 主要写入读取数据时候做加密与解密的动作 缺点: 表结构暴露 无法直接搜索 数据库文 ...

  9. 微信数据库最新的解密方式,使用C++代码解密微信加密数据库信息!

    原文转载于:https://blog.csdn.net/jiangwei0910410003/article/details/79436956 一.知识回顾 最近有人找我弄微信数据库解密的东西,其实这 ...

最新文章

  1. 如何写一个脚本语言_如何写一个Nx schematic plugin?
  2. Delphi - 使用字符串时,一个注意地方
  3. Python应用与实践【转】
  4. php session写入数据库_php session 写入数据库,phpsession
  5. workbeach约束简称_AnsysWorkbenchFluidFlow(FLUENT)经典问题.docx
  6. EXT2/EXT3/EXT4文件系统数据恢复工具开发计划
  7. 纪中在家培训总结(2020.2.1~2020.2.24)
  8. 动画原理——绘制正弦函数环绕运动椭圆运动
  9. 二叉搜索树的插入、删除、修剪、构造操作(leetcode701、450、669、108)
  10. 工作33:page值不能修改
  11. 【Selenium】之谷歌、IE、火狐浏览器各个版本的浏览器驱动下载地址
  12. 61.新的开始(最小生成树)
  13. F# 与 WPF 之多语言实现MVVM(二)
  14. 在gns3中搭建VTP演示实验
  15. 【网络流24题】星际转移问题(最大流)(网络判定)
  16. Spoon工具使用(kettle进行实时同步数据)
  17. SpringBoot整合activiti7,demo示例
  18. vue中如何使用h5自定义标签?
  19. 【Python爬虫】爬取 LOL 全英雄全皮肤图片
  20. 如何区分单色LCD液晶屏的显示模式和玻璃型号

热门文章

  1. 分享35款超酷的免费英文涂鸦字体
  2. 计算机开机后无法启动win7,win7电脑突然进不了系统如何恢复?老司机教你解决电脑开机进不了系统...
  3. [故事]女博士在京辛酸买房记:同学想读博吗?先买个房吧
  4. ipad wifi信号测试软件,总有适合你 iPad六种无线上网方式汇总
  5. 如何检查 MDAC 版本
  6. 从零开始编译OpenWrt固件
  7. 集成学习【一】:集成学习(Ensemble Learning)结合神经网络
  8. 让NETMD显示汉字
  9. 身体语言密码_“我根本不知道她到底想要什么”,《身体语言密码》告诉你
  10. 【技术分享】 ​IE浏览器漏洞利用技术的演变 ( 二 )