如何使用 sqlcipher 加密现有的 Room 数据库?
How can I encrypt the existing Room database with sqlcipher?
一段时间以来,我一直在寻找 Room
数据库安全的解决方案,并熟悉了 sqlcipher
,但找不到合适的解决方案来使用它。
终于到了地址
Link
我到了,在这个网站上的培训的帮助下,我能够做一些事情。
我是这样写代码的:
import android.content.Context;
import android.content.SharedPreferences;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SupportFactory;
import java.io.IOException;
import static ir.sharikapp.sharik.PopularMethods.randomPassword;
import static ir.sharikapp.sharik.PreferenceManager.mEditor;
import static ir.sharikapp.sharik.PreferenceManager.mPrefs;
@Database(version = 3, exportSchema = false, entities = {Transaction.class, Partners.class, Subscription.class})
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase appDatabase;
static String pass = null;
static byte[] passphrase = null;
static SupportFactory factory = null;
static SharedPreferences sharedPreferences = null;
static SharedPreferences.Editor sharedPreferencesEditor = null;
public static AppDatabase getAppDatabase(Context context) {
if (factory == null) {
if (sharedPreferences == null) {
PreferenceManager.getInstance(context);
sharedPreferences = mPrefs;
sharedPreferencesEditor = mEditor;
}
if (!sharedPreferences.contains("dbEncrypted")) {
sharedPreferencesEditor.putString("dbPass", randomPassword(50));
sharedPreferencesEditor.putString("dbEncrypted", "yes");
sharedPreferencesEditor.apply();
}
pass = sharedPreferences.getString("dbPass", null);
passphrase = SQLiteDatabase.getBytes(pass.toCharArray());
factory = new SupportFactory(passphrase);
}
if (appDatabase == null)
appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "dp_app")
.addMigrations(MIGRATION_2_3)
.allowMainThreadQueries()
.openHelperFactory(factory)
.build();
return appDatabase;
}
// Migration from 2 to 3
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database = SQLiteDatabase.openOrCreateDatabase("dp_app.db","", null);
database.query(
"ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'");
database.query("select sqlcipher_export('encrypted')");
database.query("DETACH DATABASE encrypted");
try {
database.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
public abstract SubscriptionDao getSubscriptionDao();
public abstract TransactionDao getTransactionDao();
public abstract PartnersDao getPartnersDao();
}
pass = sharedPreferences.getString("dbPass", null);
这行代码捕获了软件第一次使用时存储在sharedPreferences中的密码运行。
迁移内部和队列中
database.query("ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY
'${passphrase}'");
这可能是个问题。
另外,看到上面Link中提到的例子,迁移方法是这样写的
database = SQLiteDatabase.openOrCreateDatabase("clearDatabase.db","", null);
database.rawExecSQL(
"ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'");
database.rawExecSQL("select sqlcipher_export('encrypted')");
database.rawExecSQL("DETACH DATABASE encrypted");
database.close();
但是我无法使用database.rawExecSQL,因为我在写这段代码的时候,androidstudio在KEY下划线是错误的;这就是我使用 database.query 的原因。这会是我的错吗?
当我 运行 程序时,我得到一个错误,我把它放在它的 stackTrace 下。
net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:89)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:62)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:91)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2669)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2599)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
at ir.sharikapp.sharik.PartnersDao_Impl.idCounter(PartnersDao_Impl.java:224)
at ir.sharikapp.sharik.SplashScreen.lambda$onCreate[=12=]$SplashScreen(SplashScreen.java:65)
at ir.sharikapp.sharik.-$$Lambda$SplashScreen$OnSbGh2ZI8dgfCv5r4WYoYzo4YA.run(lambda)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6816)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1563)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1451)
有这方面资料的朋友,谢谢分享给我
我不确定你是否可以像这样使用成员变量,因为事实上,你不确定第一个变量是否在第二个变量之前被初始化。
尝试在 init 方法中初始化它们或像这样:
static String pass = null;
static byte[] passphrase = null;
static SupportFactory factory = null;
public static AppDatabase getAppDatabase(Context context) {
if (pass == null) {
pass = SplashScreen.sharedPreferences.getString("dbPass", null);
passphrase = SQLiteDatabase.getBytes(pass.toCharArray());
factory = new SupportFactory(passphrase);
}
if (appDatabase == null) {
appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "dp_app")
.addMigrations(MIGRATION_2_3)
.allowMainThreadQueries()
.openHelperFactory(factory)
.build();
}
return appDatabase;
}
单独加密数据库的每条记录,您可以在加密数据库每条记录的函数中使用此程序包encrypt。
一段时间以来,我一直在寻找 Room
数据库安全的解决方案,并熟悉了 sqlcipher
,但找不到合适的解决方案来使用它。
终于到了地址 Link 我到了,在这个网站上的培训的帮助下,我能够做一些事情。
我是这样写代码的:
import android.content.Context;
import android.content.SharedPreferences;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SupportFactory;
import java.io.IOException;
import static ir.sharikapp.sharik.PopularMethods.randomPassword;
import static ir.sharikapp.sharik.PreferenceManager.mEditor;
import static ir.sharikapp.sharik.PreferenceManager.mPrefs;
@Database(version = 3, exportSchema = false, entities = {Transaction.class, Partners.class, Subscription.class})
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase appDatabase;
static String pass = null;
static byte[] passphrase = null;
static SupportFactory factory = null;
static SharedPreferences sharedPreferences = null;
static SharedPreferences.Editor sharedPreferencesEditor = null;
public static AppDatabase getAppDatabase(Context context) {
if (factory == null) {
if (sharedPreferences == null) {
PreferenceManager.getInstance(context);
sharedPreferences = mPrefs;
sharedPreferencesEditor = mEditor;
}
if (!sharedPreferences.contains("dbEncrypted")) {
sharedPreferencesEditor.putString("dbPass", randomPassword(50));
sharedPreferencesEditor.putString("dbEncrypted", "yes");
sharedPreferencesEditor.apply();
}
pass = sharedPreferences.getString("dbPass", null);
passphrase = SQLiteDatabase.getBytes(pass.toCharArray());
factory = new SupportFactory(passphrase);
}
if (appDatabase == null)
appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "dp_app")
.addMigrations(MIGRATION_2_3)
.allowMainThreadQueries()
.openHelperFactory(factory)
.build();
return appDatabase;
}
// Migration from 2 to 3
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database = SQLiteDatabase.openOrCreateDatabase("dp_app.db","", null);
database.query(
"ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'");
database.query("select sqlcipher_export('encrypted')");
database.query("DETACH DATABASE encrypted");
try {
database.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
public abstract SubscriptionDao getSubscriptionDao();
public abstract TransactionDao getTransactionDao();
public abstract PartnersDao getPartnersDao();
}
pass = sharedPreferences.getString("dbPass", null);
这行代码捕获了软件第一次使用时存储在sharedPreferences中的密码运行。
迁移内部和队列中
database.query("ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'");
这可能是个问题。
另外,看到上面Link中提到的例子,迁移方法是这样写的
database = SQLiteDatabase.openOrCreateDatabase("clearDatabase.db","", null);
database.rawExecSQL(
"ATTACH DATABASE '${encryptedDbPath}' AS encrypted KEY '${passphrase}'");
database.rawExecSQL("select sqlcipher_export('encrypted')");
database.rawExecSQL("DETACH DATABASE encrypted");
database.close();
但是我无法使用database.rawExecSQL,因为我在写这段代码的时候,androidstudio在KEY下划线是错误的;这就是我使用 database.query 的原因。这会是我的错吗?
当我 运行 程序时,我得到一个错误,我把它放在它的 stackTrace 下。
net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:89)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:62)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:91)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2669)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2599)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
at ir.sharikapp.sharik.PartnersDao_Impl.idCounter(PartnersDao_Impl.java:224)
at ir.sharikapp.sharik.SplashScreen.lambda$onCreate[=12=]$SplashScreen(SplashScreen.java:65)
at ir.sharikapp.sharik.-$$Lambda$SplashScreen$OnSbGh2ZI8dgfCv5r4WYoYzo4YA.run(lambda)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6816)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1563)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1451)
有这方面资料的朋友,谢谢分享给我
我不确定你是否可以像这样使用成员变量,因为事实上,你不确定第一个变量是否在第二个变量之前被初始化。
尝试在 init 方法中初始化它们或像这样:
static String pass = null;
static byte[] passphrase = null;
static SupportFactory factory = null;
public static AppDatabase getAppDatabase(Context context) {
if (pass == null) {
pass = SplashScreen.sharedPreferences.getString("dbPass", null);
passphrase = SQLiteDatabase.getBytes(pass.toCharArray());
factory = new SupportFactory(passphrase);
}
if (appDatabase == null) {
appDatabase = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "dp_app")
.addMigrations(MIGRATION_2_3)
.allowMainThreadQueries()
.openHelperFactory(factory)
.build();
}
return appDatabase;
}
单独加密数据库的每条记录,您可以在加密数据库每条记录的函数中使用此程序包encrypt。