Android 使用sqlcipher加密和解密数据库(包括加密和解密已有的数据库,还有如何查看数据库教程)

这篇具有很好参考价值的文章主要介绍了Android 使用sqlcipher加密和解密数据库(包括加密和解密已有的数据库,还有如何查看数据库教程)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

我们知道Android系统有一个内嵌的SQLite数据库,并且提供了一整套的API用于对数据库进行增删改查操作,SQLite是一个轻量级的、跨平台的、开源的嵌入式数据库引擎,也是一个关系型的的使用SQL语句的数据库引擎,读写效率高、资源消耗总量少、延迟时间少,使其成为移动平台数据库的最佳解决方案(如Android、iOS)
但是Android上自带的SQLite数据库是没有实现加密的,我们可以通过Android Studio直接导出应用创建的数据库文件,然后通过如SQLiteStudio 这种可视化工具打开数据库文件进行查看数据库的表结构,以及数据。

不过,使用SQLite来存储数据却存在着一个问题,正常情况下我们不能看到Android手机数据库文件,但是Android手机Root过,而Root过的手机都可以进入到/data/data/<package_name>/databases目录下(或者直接使用Android Studio自带的模拟器也能进入到/data/data/<package_name>/databases目录下),在这里就可以查看到数据库中存储的所有数据。如果是一般的数据还好,但是当涉及到一些账号密码,或者聊天内容的时候,我们的程序就会面临严重的安全漏洞隐患。那么今天,就让我们一起研究一下如何借助sqlcipher来解决这个安全性问题。

sqlcipher是一个在SQLite基础之上进行扩展的开源数据库,它主要是在SQLite的基础之上增加了数据加密功能,如果我们在项目中使用它来存储数据的话,就可以大大提高程序的安全性。sqlcipher支持很多种不同的平台,这里我们要学习的自然是Android中sqlcipher的用法了。

注意:实际上是对原有的db文件数据拷贝了一份到新的db文件并进行了加密,并不是对db文件的字段进行加密。

1.首先创建一个数据库(这里以Room数据库为例,详见Room数据库的使用与升级(详细介绍了增删改查,关于查询,各种查询方式都有介绍)_ErwinNakajima的博客-CSDN博客)。

2.引入sqlcipher依赖,在app的build.gradle中添加依赖,并同步项目。

dependencies {
implementation "net.zetetic:android-database-sqlcipher:4.4.3@aar"
}

3.把第一步中的Room数据库文件AppRoomDataBase改写一下,并把version在原基础之上加1。

package com.phone.library_common.room

import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.phone.library_common.BaseApplication
import com.phone.library_common.BuildConfig
import com.phone.library_common.JavaGetData
import com.phone.library_common.manager.LogManager
import com.phone.library_common.manager.SharedPreferencesManager
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SQLiteDatabaseHook
import net.sqlcipher.database.SupportFactory

@Database(entities = [Book::class], version = 3)
abstract class AppRoomDataBase : RoomDatabase() {
    //创建DAO的抽象类
    abstract fun bookDao(): BookDao

    companion object {
        private val TAG = AppRoomDataBase::class.java.simpleName

        val DATABASE_ENCRYPT_KEY =
            JavaGetData.nativeDatabaseEncryptKey(BaseApplication.get(), BuildConfig.IS_RELEASE)
        val passphrase = SQLiteDatabase.getBytes(DATABASE_ENCRYPT_KEY.toCharArray())
        val factory = SupportFactory(passphrase, object : SQLiteDatabaseHook {
            override fun preKey(database: SQLiteDatabase?) {
            }

            override fun postKey(database: SQLiteDatabase?) {
//                database?.execSQL("PRAGMA cipher_page_size = 1024")
//                database?.execSQL("PRAGMA kdf_iter = 64000")
//                database?.execSQL("PRAGMA cipher_hmac_algorithm = HMAC_SHA1")
//                database?.execSQL("PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1")
            }
        }, true)

        const val DATABASE_NAME = "simple_app.db"
        const val DATABASE_ENCRYPT_NAME = "simple_encrypt_app.db"
        const val DATABASE_DECRYPT_NAME = "simple_decrypt_app.db"


        @Volatile
        private var databaseInstance: AppRoomDataBase? = null

        @Synchronized
        @JvmStatic
        fun get(): AppRoomDataBase {
            if (databaseInstance == null) {
//                databaseInstance = Room.databaseBuilder(
//                    BaseApplication.get(),
//                    AppRoomDataBase::class.java,
//                    DATABASE_NAME
//                )
//                    .allowMainThreadQueries()//允许在主线程操作数据库,一般不推荐;设置这个后主线程调用增删改查不会报错,否则会报错
                    .openHelperFactory(factory)
//                    .build()


                databaseInstance = Room.databaseBuilder(
                    BaseApplication.get(),
                    AppRoomDataBase::class.java,
                    DATABASE_ENCRYPT_NAME
                )
                    .allowMainThreadQueries()//允许在主线程操作数据库,一般不推荐;设置这个后主线程调用增删改查不会报错,否则会报错
                    .openHelperFactory(factory)
                    .build()

                val dataEncryptTimes = SharedPreferencesManager.get("dataEncryptTimes", "0")
                if ("0".equals(dataEncryptTimes)) {
                    encrypt(
                        DATABASE_ENCRYPT_NAME,
                        DATABASE_NAME,
                        DATABASE_ENCRYPT_KEY
                    )
                    SharedPreferencesManager.put("dataEncryptTimes", "1")
                }
            }
            return databaseInstance!!
        }


        /**
         * 加密数据库
         * @param encryptedName 加密后的数据库名称
         * @param name 要加密的数据库名称
         * @param key 密码
         */
        @JvmStatic
        fun encrypt(encryptedName: String, name: String, key: String) {
            try {
                val databaseFile = BaseApplication.get().getDatabasePath(name)
                LogManager.i(TAG, "databaseFile*****${databaseFile.absolutePath}")
                val database: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(databaseFile, "", null) //打开要加密的数据库

                /*String passwordString = "123456"; //只能对已加密的数据库修改密码,且无法直接修改为“”或null的密码
                database.changePassword(passwordString.toCharArray());*/
                val encrypteddatabaseFile =
                    BaseApplication.get().getDatabasePath(encryptedName) //新建加密后的数据库文件
                LogManager.i(TAG, "encrypteddatabaseFile*****${encrypteddatabaseFile.absolutePath}")
                //deleteDatabase(SDcardPath + encryptedName);

                //连接到加密后的数据库,并设置密码
                database.rawExecSQL(
                    String.format(
                        "ATTACH DATABASE '%s' as " + encryptedName.split(".")[0] + " KEY '" + key + "';",
                        encrypteddatabaseFile.getAbsolutePath()
                    )
                )
                //输出要加密的数据库表和数据到加密后的数据库文件中
                database.rawExecSQL("SELECT sqlcipher_export('" + encryptedName.split(".")[0] + "');")
                //断开同加密后的数据库的连接
                database.rawExecSQL("DETACH DATABASE " + encryptedName.split(".")[0] + ";")

                //打开加密后的数据库,测试数据库是否加密成功
                val encrypteddatabase: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(encrypteddatabaseFile, key, null)
                //encrypteddatabase.setVersion(database.getVersion());
                encrypteddatabase.close() //关闭数据库
                database.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        /**
         * 解密数据库
         * @param decryptedName 解密后的数据库名称
         * @param name 要解密的数据库名称
         * @param key 密码
         */
        @JvmStatic
        fun decrypt(decryptedName: String, name: String, key: String) {
            try {
                val databaseFile = BaseApplication.get().getDatabasePath(name)
                val database: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(databaseFile, key, null)
                val decryptedDatabaseFile = BaseApplication.get().getDatabasePath(decryptedName)
                //deleteDatabase(SDcardPath + decryptedName);

                //连接到解密后的数据库,并设置密码为空
                database.rawExecSQL(
                    String.format(
                        "ATTACH DATABASE '%s' as " + decryptedName.split(".")[0] + " KEY '';",
                        decryptedDatabaseFile.getAbsolutePath()
                    )
                )
                database.rawExecSQL("SELECT sqlcipher_export('" + decryptedName.split(".")[0] + "');")
                database.rawExecSQL("DETACH DATABASE " + decryptedName.split(".")[0] + ";")

                val decrypteddatabase: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(decryptedDatabaseFile, "", null)
                //decrypteddatabase.setVersion(database.getVersion());
                decrypteddatabase.close()
                database.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

    }


}

4.加密数据库方法(实际上是把原有simple_app.db文件数据拷贝了一份到新的simple_encrypt_app.db文件,并给新的simple_encrypt_app.db文件设置了密码)。


        /**
         * 加密数据库
         * @param encryptedName 加密后的数据库名称
         * @param name 要加密的数据库名称
         * @param key 密码
         */
        @JvmStatic
        fun encrypt(encryptedName: String, name: String, key: String) {
            try {
                val databaseFile = BaseApplication.get().getDatabasePath(name)
                LogManager.i(TAG, "databaseFile*****${databaseFile.absolutePath}")
                val database: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(databaseFile, "", null) //打开要加密的数据库

                /*String passwordString = "123456"; //只能对已加密的数据库修改密码,且无法直接修改为“”或null的密码
                database.changePassword(passwordString.toCharArray());*/
                val encrypteddatabaseFile =
                    BaseApplication.get().getDatabasePath(encryptedName) //新建加密后的数据库文件
                LogManager.i(TAG, "encrypteddatabaseFile*****${encrypteddatabaseFile.absolutePath}")
                //deleteDatabase(SDcardPath + encryptedName);

                //连接到加密后的数据库,并设置密码
                database.rawExecSQL(
                    String.format(
                        "ATTACH DATABASE '%s' as " + encryptedName.split(".")[0] + " KEY '" + key + "';",
                        encrypteddatabaseFile.getAbsolutePath()
                    )
                )
                //输出要加密的数据库表和数据到加密后的数据库文件中
                database.rawExecSQL("SELECT sqlcipher_export('" + encryptedName.split(".")[0] + "');")
                //断开同加密后的数据库的连接
                database.rawExecSQL("DETACH DATABASE " + encryptedName.split(".")[0] + ";")

                //打开加密后的数据库,测试数据库是否加密成功
                val encrypteddatabase: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(encrypteddatabaseFile, key, null)
                //encrypteddatabase.setVersion(database.getVersion());
                encrypteddatabase.close() //关闭数据库
                database.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

5.解密数据库方法(实际上是把原有simple_encrypt_app.db文件数据拷贝了一份到新的simple_decrypt_app.db文件,并给新的simple_decrypt_app.db文件取消了密码)。

/**
         * 解密数据库
         * @param decryptedName 解密后的数据库名称
         * @param name 要解密的数据库名称
         * @param key 密码
         */
        @JvmStatic
        fun decrypt(decryptedName: String, name: String, key: String) {
            try {
                val databaseFile = BaseApplication.get().getDatabasePath(name)
                val database: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(databaseFile, key, null)
                val decryptedDatabaseFile = BaseApplication.get().getDatabasePath(decryptedName)
                //deleteDatabase(SDcardPath + decryptedName);

                //连接到解密后的数据库,并设置密码为空
                database.rawExecSQL(
                    String.format(
                        "ATTACH DATABASE '%s' as " + decryptedName.split(".")[0] + " KEY '';",
                        decryptedDatabaseFile.getAbsolutePath()
                    )
                )
                //输出要解密的数据库表和数据到解密后的数据库文件中
                database.rawExecSQL("SELECT sqlcipher_export('" + decryptedName.split(".")[0] + "');")
                database.rawExecSQL("DETACH DATABASE " + decryptedName.split(".")[0] + ";")

                val decrypteddatabase: SQLiteDatabase =
                    SQLiteDatabase.openOrCreateDatabase(decryptedDatabaseFile, "", null)
                //decrypteddatabase.setVersion(database.getVersion());
                decrypteddatabase.close()
                database.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

6.新安装的App(覆盖安装)在初始化数据库的时候,会进行数据库加密操作(只加密一次),然后会生成一份新的simple_decrypt_app.db文件。

        @Synchronized
        @JvmStatic
        fun get(): AppRoomDataBase {
            if (databaseInstance == null) {
//                databaseInstance = Room.databaseBuilder(
//                    BaseApplication.get(),
//                    AppRoomDataBase::class.java,
//                    DATABASE_NAME
//                )
//                    .allowMainThreadQueries()//允许在主线程操作数据库,一般不推荐;设置这个后主线程调用增删改查不会报错,否则会报错
                    .openHelperFactory(factory)
//                    .build()


                databaseInstance = Room.databaseBuilder(
                    BaseApplication.get(),
                    AppRoomDataBase::class.java,
                    DATABASE_ENCRYPT_NAME
                )
                    .allowMainThreadQueries()//允许在主线程操作数据库,一般不推荐;设置这个后主线程调用增删改查不会报错,否则会报错
                    .openHelperFactory(factory)
                    .build()

                val dataEncryptTimes = SharedPreferencesManager.get("dataEncryptTimes", "0")
                if ("0".equals(dataEncryptTimes)) {
                    encrypt(
                        DATABASE_ENCRYPT_NAME,
                        DATABASE_NAME,
                        DATABASE_ENCRYPT_KEY
                    )
                    SharedPreferencesManager.put("dataEncryptTimes", "1")
                }
            }
            return databaseInstance!!
        }

初始化数据库的方法(注意:加密或解密数据库时候,不要执行增删改查操作,不然有可能造成App崩溃)。

val appRoomDataBase = AppRoomDataBase.get()

7.查看加密过的数据库文件(先运行项目看一下加密前的数据库文件simple_app.db,然后改成加密方式,运行项目,会生成加密后的数据库文件simple_encrypt_app.db)。

Android查看加密前的数据库文件和加密后的数据库文件视频

如对此有疑问,请联系qq1164688204。

推荐Android开源项目

项目功能介绍:原本是RxJava2 和Retrofit2 项目,现已更新使用Kotlin+RxJava2+Retrofit2+MVP架构+组件化和
Kotlin+Retrofit2+协程+MVVM架构+组件化, 添加自动管理token 功能,添加RxJava2 生命周期管理,集成极光推送、阿里云Oss对象存储和高德地图定位功能。

项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2文章来源地址https://www.toymoban.com/news/detail-734936.html

到了这里,关于Android 使用sqlcipher加密和解密数据库(包括加密和解密已有的数据库,还有如何查看数据库教程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用python3的sqlalchemy+sqlcipher3对sqlite3数据加密,使用pysqlcipher3编译打建环境,并写项目测试

    一、背景 最近有个想法,想写一个软件,前期本来想用java的springboot加vue来实现,数据库选sqlite来存储,但在用spingboot框架搭好之后,感觉这款软件更适合用python来写,java不适windows桌面系统,最终选择了pthon使用tkinter gui来实现。sqlite数据库方便是方便,但开源版本不能设置

    2024年02月12日
    浏览(49)
  • Spring Boot使用jasypt处理数据库账号密码等数据加密问题

    在我们业务场景中,项目中的application.yml 配置文件比如数据库账号密码,的各种链接的username,password的值都是明文的,存在一定的安全隐患,可以使用jasypt 加密框架的方式进行明文加密,进而使得我们项目更加安全 注意这里排除了mybatis-plus的包可能是项目中有冲突依赖,

    2024年02月06日
    浏览(55)
  • Spring Boot项目使用 jasypt 加密组件进行加密(例如:数据库、服务的Key、等等进行加密)

    🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝 🍓 更多文章请点击 密码配置项都不加密? 想啥呢? 一

    2024年02月07日
    浏览(75)
  • python使用pysqlcipher3对sqlite数据库进行加密

    python对很多项目都需要对sqlite数据库的数据进行加密,最流行的加密方式是使用pysqlcipher3,当前使用的python版本为3.7,本博文是直接使用pysqlcipher3在项目上的应用,使用的是已编译好的pysqlcipher3包,如果你需要pysqlcipher3,在后面有下载地址,下载后,可以直接使用,不需要再

    2024年04月13日
    浏览(40)
  • 【Android】Room数据库的使用

    Room 是在 SQLite 的基础上推出的 Android 库,它是 Google 官方对数据库操作的推荐方式。使用 Room 可以更方便、高效地操作 SQLite 数据库。 添加依赖 在使用 Room 之前,需要在项目中添加 Room 相关的依赖。在 build.gradle 文件中添加以下依赖: 在上面的依赖中,我们添加了 room-runti

    2024年02月09日
    浏览(49)
  • Android之SQLite数据库使用

    SQLite是Android系统集成的一个轻量级的数据库。 Android提供了 SQLiteDatabase代表一个数据库 (底层就是一个数据库文件),一旦应用程序获得了代表指定数据库的SQLiteDatabase对象,接下来可通过SQLiteDatabase对象来管理、操作数据库了。 Android为了让我们能够更加方便地管理数据库,

    2024年02月16日
    浏览(50)
  • 数据库密码加解密

    为符合数据安全,针对数据库密码进行加密以防止数据库密码明文展示而引发的安全隐患。 加密Jar包: 数据库密码加密Jar包-Java文档类资源-CSDN下载 使用详解(环境为JDK1.7+): 1.通过命令 java  -jar xx.jar运行jar包,如图: 2.输入数据库明文对数据库密码加密,如图:   红框中

    2024年02月13日
    浏览(38)
  • 微信数据库解密

    根据网上的教程,整理了从PC客户端和Android手机两种设备获取数据库文件并解密的过程。 实验版本V3.7.6 查找数据库文件位置 因为存储位置不相同,推荐直接使用 Everything 搜索 MicroMsg.db 。 这个数据库中存储了联系人、群组、最后一次会话等信息。真正的聊天记录再Multi文件夹

    2024年01月19日
    浏览(42)
  • Android Studio使用SQLite数据库

    1.能使用SQLiteDatabase类操作数据库与表 2.能使用SQLiteDatabaseHelper类操作数据库与表 无论是安卓应用还是苹果应用,都提供了本地轻量级数据库——SQLite,可以创建和删除数据库,还能对数据表进行增删改查操作。 SQLite由SQL编译器、内核、后端以及附件几个部分构成。SQLite通过

    2024年02月01日
    浏览(47)
  • Android开发——SQLite数据库的使用

    1、SQLite的特性 SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置。 SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。

    2024年02月15日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包