Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

集成WCDB后希望能实现解密数据库 #36

Closed
weilixin opened this issue Jun 16, 2017 · 10 comments
Closed

集成WCDB后希望能实现解密数据库 #36

weilixin opened this issue Jun 16, 2017 · 10 comments
Labels

Comments

@weilixin
Copy link

你好,我们应用原来使用cipher android加密,现在集成了wcdb。为了方便调试问题,我们在debug版本中支持解密已经加密的数据库,请问wcdb如何将已经加密的数据库转为非加密数据库呢?

原来的方法,如下:
1.打开已经加密的数据库,创建临时文件
2.将数据迁移到临时文件(newFile就是临时文件)
// 开始数据迁移
try {
// db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 4000;");
db.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
newFile.getAbsolutePath(), (isDecode ? ""/* 无密 */ : password)));
db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
db.rawExecSQL("DETACH DATABASE encrypted;");
db.close();
} catch (Exception e) {
LogUtil.e(TAG, "CipherDB::startDataBaseMigration OMG ...DataMigration has exception. "
+ e.getMessage());
throw new StopRequestException(DATA_TRANSFER_DATA_MIGRATION_ERROR, e);
}
3.临时文件生成新数据库

@RingoD RingoD added the Java label Jun 16, 2017
@John-He-928
Copy link
Collaborator

这个跟原来一样的,执行这些SQL语句就能解密

@weilixin
Copy link
Author

weilixin commented Jun 19, 2017

你好,我尝试解密数据库还是失败,烦请帮忙看一下原因,多谢!

打开wcdb加密数据库代码如下:
db = com.tencent.wcdb.database.SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
(isDecode ? password.getBytes() : null/* 无密 */), cipher, null,
SQLiteDatabase.OPEN_READWRITE, new DatabaseErrorHandler() {
@OverRide
public void onCorruption(com.tencent.wcdb.database.SQLiteDatabase dbObj) {
LogUtil.e(TAG, "CipherDB::startDataBaseMigration2::onCorruption1");
}
});

创建临时文件暂存代码:
newFile = File.createTempFile(ENCRYPTED_DATABASE_PREFIX, TEMP_ENCRYPTED_DATABASE_PREFIX,
context.getCacheDir());

数据迁移代码:
try {
// db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 4000;");
db.execSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s';",
newFile.getAbsolutePath(), (isDecode ? ""/* 无密 */ : password)));
db.execSQL("SELECT sqlcipher_export('encrypted')");
db.execSQL("DETACH DATABASE encrypted;");
db.close();
} catch (Exception e) {
LogUtil.e(TAG, "CipherDB::startDataBaseMigration OMG ...DataMigration has exception. "
+ e.getMessage());
throw new StopRequestException(DATA_TRANSFER_DATA_MIGRATION_ERROR, e);
}

异常日志,如下:

06-19 16:17:22.214 7185-7185/com.wlx.debug W/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::onItemClick: common_new
06-19 16:17:22.215 7185-7185/com.wlx.debug D/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration starting... databaseName: common_new
06-19 16:17:22.215 7185-7185/com.wlx.debug D/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration originalFile path: /data/user/0/com.wlx.debug/databases/common_new

06-19 16:17:22.215 7185-7185/com.wlx.debug I/WCDB.SQLiteConnectionPool: Max connection pool size is 1.
06-19 16:17:22.216 7185-7185/com.wlx.debug I/WCDB: Initialize SQLite connection module 'MMFTS'...
06-19 16:17:22.216 7185-7185/com.wlx.debug I/WCDB.SQLiteConnection: Opened connection 0x9b8f6808 with label '/data/user/0/com.wlx.debug/databases/common_new'
06-19 16:17:22.216 7185-7185/com.wlx.debug I/WCDB.SQLiteConnection: sqlite3_key verification passed.

06-19 16:17:22.254 7185-7185/com.wlx.debug W/WCDB.SQLite: [SQLite ErrCode: 5] statement aborts at 1: [PRAGMA journal_mode=PERSIST] database is locked
06-19 16:17:22.255 7185-7185/com.wlx.debug W/WCDB.SQLiteConnectionPool: Connections: 0 active, 0 idle, 0 available.
06-19 16:17:22.257 7185-7185/com.wlx.debug I/WCDB.SQLiteConnection: executeForString took 1ms - failed, sql="PRAGMA journal_mode=PERSIST", exception="database is locked (code 5, errno 0): "
06-19 16:17:22.257 7185-7185/com.wlx.debug W/WCDB.SQLiteConnection: Could not change the database journal mode of '/data/user/0/com.wlx.debug/databases/common_new' from 'wal' to 'PERSIST' because the database is locked. This usually means that there are other open connections to the database which prevents the database from enabling or disabling write-ahead logging mode. Proceeding without changing the journal mode.
06-19 16:17:22.258 7185-7185/com.wlx.debug D/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration originalFile version: 65
06-19 16:17:22.266 7185-7185/com.wlx.debug D/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration newFile path: /data/user/0/com.wlx.debug/cache/tmp_common

06-19 16:17:23.852 7185-7185/com.wlx.debug I/WCDB.SQLiteConnection: executeForChangedRowCount took 1584ms - failed, sql="SELECT sqlcipher_export('encrypted')", exception="unknown error (code 0, errno -1): Queries can be performed using SQLiteDatabase query or rawQuery methods only.", changedRows=0

06-19 16:17:23.852 7185-7185/com.wlx.debug E/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration OMG ...DataMigration has exception. unknown error (code 0, errno -1): Queries can be performed using SQLiteDatabase query or rawQuery methods only.
06-19 16:17:23.852 7185-7185/com.wlx.debug E/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration --- > Aborting request for database migration common_new: com.tencent.wcdb.database.SQLiteException: unknown error (code 0, errno -1): Queries can be performed using SQLiteDatabase query or rawQuery methods only.
06-19 16:17:23.853 7185-7185/com.wlx.debug D/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration finally --- > 103

@John-He-928
Copy link
Collaborator

@weilixin 你可以参看以下 sample-encryptdbEncryptedDBHelper.onCreate() 里演示了如何迁移数据库,这个对加密和非加密都是一样的,你可以正常打开加密 DB,ATTACH 一个空的非加密 DB,然后用
sqlcipher_export来迁移。

WCDB的 sqlcipher_export 经过扩展支持两个参数,分别是 导出到哪个DB从哪个DB导出,例子里是从非加密到加密,把参数反过来即可解密。

WCDB接口和限制跟 Android Framework 一致,和 SQLCipher Android 在限制上有一点点不同,但原理是一样的(包括 kdf_iter 等参数),SQL语句不用改,可能要稍微改一下调用代码。

@weilixin
Copy link
Author

weilixin commented Jun 20, 2017

@John-He-928 感谢帮助,还是有些问题。
String sql = String.format("ATTACH DATABASE %s AS encrypted KEY '%s';",
DatabaseUtils.sqlEscapeString(originalFile.getPath()), password);
db.execSQL(sql);

PS:失败原因应该是密码不对,但实际密码没问题,肯定正确,传入字符串还是byte数组呢?

异常日志,如下: (密码肯定正确,58位数字大小写英文混合)
06-20 10:00:38.310 28755-28755/com.wlx.debug W/WCDB.SQLite: [SQLite ErrCode: 26] statement aborts at 4: [ATTACH DATABASE '/data/user/0/com.wlx.debug/databases/common_new' AS encrypted KEY '733491015b519f491fc2601654c44de8sIO5reWAbAsR1Pbq13ws567612';] file is encrypted or is not a
06-20 10:00:38.311 28755-28755/com.wlx.debug I/WCDB.SQLiteConnection: executeForChangedRowCount took 253ms - failed, sql="ATTACH DATABASE '/data/user/0/com.wlx.debug/databases/common_new' AS encrypted KEY '733491015b519f491fc2601654c44de8sIO5reWAbAsR1Pbq13ws567612';", exception="file is encrypted or is not a database (code 26, errno 0): ", changedRows=0
06-20 10:00:38.316 28755-28755/com.wlx.debug E/wlx: main:SqliteCipherLogic [LOGIN_READLY]: CipherDB::startDataBaseMigration2::onCorruption

@John-He-928
Copy link
Collaborator

你可以打开加密的,然后attach没加密的,然后export

@weilixin
Copy link
Author

@John-He-928 感谢支持!
抱歉,按照你提示的做法尝试还是失败了,麻烦再帮看一下。数据库迁移代码如下:(db2是非加密的数据库,db是加密的数据库)
// 开始数据迁移
try {
db.execSQL(String.format("ATTACH DATABASE '%s' AS unencrypted KEY '%s';",
db2.getPath(), ""));
db.execSQL(String.format("SELECT sqlcipher_export('unencrypted', '%s')",
db.getPath()));
db.execSQL("DETACH DATABASE unencrypted;");
db.close();
} catch (Exception e) {
LogUtil.e(TAG, "CipherDB::startDataBaseMigration OMG ...DataMigration has exception. "
+ e.getMessage());
throw new StopRequestException(DATA_TRANSFER_DATA_MIGRATION_ERROR, e);
}

失败日志如下:
// db-tmp是非加密数据库, common_new是加密的数据库。希望将common_new数据转移到非加密的库db-tmp
06-21 10:19:18.942 21700-21700/com.wlx.debug I/WCDB.SQLiteConnection: Opened connection 0x939aca08 with label '/data/user/0/com.wlx.debug/cache/db-tmp'
06-21 10:19:18.944 21700-21700/com.wlx.debug D/wlx: main:SqliteCipherLogic [CONNECTION_OK]: CipherDB::startDataBaseMigration newFile path: /data/user/0/com.wlx.debug/cache/db-tmp
06-21 10:19:18.947 21700-21700/com.wlx.debug W/WCDB.SQLite: [SQLite ErrCode: 1] near "/": syntax error
06-21 10:19:18.947 21700-21700/com.wlx.debug W/WCDB.SQLite: [SQLite ErrCode: 1] statement aborts at 1: [SELECT sqlcipher_export('unencrypted', '/data/user/0/com.wlx.debug/databases/common_new')] SQL logic error or missing database
06-21 10:19:18.948 21700-21700/com.wlx.debug I/WCDB.SQLiteConnection: executeForChangedRowCount took 1ms - failed, sql="SELECT sqlcipher_export('unencrypted', '/data/user/0/com.wlx.debug/databases/common_new')", exception="SQL logic error or missing database (code 1, errno 0): ", changedRows=0
06-21 10:19:18.948 21700-21700/com.wlx.debug E/wlx: main:SqliteCipherLogic [CONNECTION_OK]: CipherDB::startDataBaseMigration OMG ...DataMigration has exception. SQL logic error or missing database (code 1, errno 0):

@John-He-928
Copy link
Collaborator

首先你需要明白几点:

  1. ATTACH 操作本身就会打开 DB,因此 ATTACH 之前不需要打开,如果 db2 是你之前已经用 open 等接口打开并得到的 SQLiteDatabase 对象,那这没有必要,直接传路径就行了。
  2. sqlcipher_export 接受的参数是打开或 ATTACH 的名称,而不是路径,ATTACH上去的,名称就是你自己起的名称(比如你的 unencrypted,或者例子里的 old),如果是主DB(open时传入路径直接打开的),那它有一个固定的名称 'main'。
  3. sqlcipher_export 的作用是 将第二个参数的 DB 的内容,完整复制到第一个参数的 DB 里面,是导出还是导入,是加密还是解密,完全取决于这两个 DB 的配置。
  4. WCDB 的 execSQL 函数只能执行 INSERT、UPDATE、DELETE 等命令,不能执行 SELECT,因为 SELECT 一般是返回结果的。你可以换一个调用方式解决这个问题,最简单是例子里的
DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('main', 'old');", null);

它的意思是查询并返回一个字符串,实际上 sqlcipher_export 并不会返回字符串,但不需要理会,忽略返回值即可,里面的语句已经执行了。

所以我觉得你可以这么操作:

// 先open了加密DB,得到 "db" 对象

// 将非加密DB挂载到 "db"
String sql = String.format("ATTACH DATABASE %s AS old KEY '';",
    DatabaseUtils.sqlEscapeString(oldDbFile.getPath()));
db.execSQL(sql);

// 将数据从 "main"(加密db) 迁移到 "old"(非加密db)
db.beginTransaction();
DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('old', 'main');", null);
db.setTransactionSuccessful();
db.endTransaction();

// 将old脱离
db.execSQL("DETACH DATABASE old;");

有没有觉得很像例子?其实就是将sqlcipher_export两个参数调换一下。

@weilixin
Copy link
Author

可以了,感谢John细致耐心讲解。赞!!!

@Guang1234567
Copy link

@John-He-928

可以通过 sqlcipher_export 来改密码吗?

// 先open了加密DB,得到 "db" 对象

// 将加密DB (old)挂载到 "db"
String sql = String.format("ATTACH DATABASE %s AS old KEY 'I am new password';",
DatabaseUtils.sqlEscapeString(oldDbFile.getPath()));
db.execSQL(sql);

// 将数据从 "main"(加密db) 迁移到 "old"(加密db)
db.beginTransaction();
DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('old', 'main');", null);
db.setTransactionSuccessful();
db.endTransaction();

// 将old脱离
db.execSQL("DETACH DATABASE old;");

@1716574336
Copy link

你好,我最近也开始使用WCDB,在集成时出现了个问题,所以向您请教下。
我这边有个本地的数据库文件 local.db,通过 WCDB 加密后得出的 encrypted.db,这时用 sample-encryptdb demo 中的 EncryptedDBHelper 来处理没问题。然后把 encrypted.db 从设备导出来,放到项目里面做一个加密的数据库文件,然后进行解密操作并读取由 encrypted.db 解密迁移到的明文数据库 plaintext.db,这时用获取到的 plaintext.db 的 SQLiteDatabase 对象来进行数据库操作会报错,错误信息说没有这样的表(no such table),然后看了下文件大小,发现原来的 local.db 和 encrypted.db 都是3.2M,而这个 plaintext.db 为 4kb,所以想请教下这要如何处理,代码如下:

this.dealTempDB()
USER_DATA_BASE = SQLiteDatabase.openOrCreateDatabase(mContext.getDatabasePath(“plaintext.db”), null, null, null)

private fun dealTempDB() {
// 先 open了加密DB,得到 "db" 对象
val encryptedDB = SQLiteDatabase.openOrCreateDatabase(mContext.getDatabasePath("encrypted.db"),
"password".toByteArray(), null, null)
// 将非加密DB挂载到 "db"
val sql = String.format("ATTACH DATABASE %s AS unencrypted KEY '';",
DatabaseUtils.sqlEscapeString(File(“data/data/com.xxx.xxx” + “plaintext.db”).path))
encryptedDB.execSQL(sql)
// 将数据从 "main"(加密db) 迁移到 "unencrypted"(非加密db)
encryptedDB.beginTransaction()
DatabaseUtils.stringForQuery(encryptedDB, "SELECT sqlcipher_export('unencrypted', 'main');", null)
encryptedDB.setTransactionSuccessful()
encryptedDB.endTransaction()
// 将 unencrypted 脱离
encryptedDB.execSQL("DETACH DATABASE unencrypted;")
encryptedDB.close()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants