Android 使用SQLCipher进行数据库加密

在Android中通常使用SQLCipher对数据库进行加密。

SQLCipher是一个开源的SQLite加密扩展,支持对db文件进行256位的AES加密。

SQLCipher 官网:

https://www.zetetic.net/sqlcipher/

目前使用的是greenDaoAndroid数据库包,这个包目前是支持SQLCipher加密功能的,而且使用起来相当简单。

首先,在 Android 上添加 SQLCipher 依赖,在 build.gradle 中的 dependencies 里添加

dependencies {
  /* 数据库加密框架 */
  implementation 'net.zetetic:android-database-sqlcipher:3.5.7@aar'
}

然后,需要在使用时设置一个密码就行了,使用起来没什么难度

// 初始化数据库信息
devOpenHelper = new MyOpenHelper(MyApp.getContext(), DATABASE_NAME, null);
//mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
//数据库加密
mDaoMaster = new DaoMaster(devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD));

就这样,数据库加密就做完了。

但是,这仅仅是对新建数据库来说,如果你之前就有已经在使用中的数据库,那么如果你直接运行项目,就会直接报错,数据库打开失败

Caused by: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master;

这是因为你你之前的数据库没有进行加密,现在是直接使用已加密的方法去使用,这样肯定是不行的。

当然,解决起来也不难,你只需要对已有的数据库进行加密,然后在调用就行了。

对已有数据库进行加密的方法如下:

/**
 * 对已有未加密数据库进行加密处理
 */
public class DataBaseEncrypt {
    private static volatile DataBaseEncrypt nInstance;
    private Boolean isOpen = true;

    public DataBaseEncrypt() {
    }

    public static DataBaseEncrypt getInstences() {
        if (nInstance == null) {
            synchronized (DataBaseEncrypt.class) {
                if (nInstance == null) {
                    nInstance = new DataBaseEncrypt();
                }
            }
        }
        return nInstance;
    }

      /**
     * 如果已有未加密的数据库旧表 先加密已有未加密数据库
     *
     * @param context
     * @param passphrase
     */
    public void encrypt(Context context, String dbName, String passphrase) {
        File file = context.getDatabasePath(dbName);
        if (file.exists() && isOpen) {
            try {
                //创建临时数据库文件
                File newFile = File.createTempFile("sqlcipherutils", "tmp", context.getCacheDir());
                //对没有加密的数据库进行加密操作
                SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READWRITE);
                db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';", newFile.getAbsolutePath(), passphrase));
                db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
                db.rawExecSQL("DETACH DATABASE encrypted;");
                int version = db.getVersion();
                db.close();

                //对已加密过的数据库设置版本号
                db = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase, null, SQLiteDatabase.OPEN_READWRITE);
                db.setVersion(version);
                db.close();
                file.delete();
                //对加密后的数据库进行重新命名
                newFile.renameTo(file);
                isOpen = false;
            } catch (Exception e) {
                isOpen = false;
            }
        }
    }

}

然后,在调用时如果能够正常调用直接返回,如果不能说明数据库没有加密过,直接调用加密方法

    /**
     * 如果对已有数据进行加密,需要先对原有数据库进行加密操作
     * @return
     */
    public synchronized Database getWritableDatabase() {
        try {
            return devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
            //尝试加密后再打开
            DataBaseEncrypt.getInstences().encrypt(MyApp.getContext(), DATABASE_NAME, DATABASE_SQLCIPHER_PASSWORD);
            return devOpenHelper.getEncryptedWritableDb(DATABASE_SQLCIPHER_PASSWORD);
        }
    }

最后,替换一下方法

  // 初始化数据库信息
  devOpenHelper = new MyOpenHelper(MyApp.getContext(), DATABASE_NAME, null);
  //mDaoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
  //数据库加密
  mDaoMaster = new DaoMaster(getWritableDatabase());

到此,对数据的加密工作基本完成,不管是直接加密还是对已有数据库进行加密都没有问题。

猜你喜欢

转载自blog.csdn.net/u014571139/article/details/102499777
今日推荐