I was using a database created in “DB Browser (SQLite)” for my application. Now I want to protect my database data by encrypting it. I have transferred my database data to a new database created in “DB Browser (SQLCipher)”. My application runs on Android OS. The problem is that my application cannot find my new encrypted database. There is no error in the code. My application has all the information about the database. What could be the problem? Could it be a difference in versions?
Hi @dev874,
This doesn’t sound specific to SQLCipher, but rather Android development. Are you bundling the SQLCipher encrypted database with your APK as an asset? If so, you’ll need to extract that asset manually to the databases
folder in order to access it.
Hello. Thanks for your reply. I am trying to open a SQLCipher database through my Android application. But my application is showing the message “An error occurred while opening the SQLCipher database!”. My application is doing the work of finding and copying the database. It is encountering a problem while trying to open it. There is no problem when I open my database in “DB Browser (SQLCipher)”. The password and KDF iterations information are included in my application. (I am using “MainActivity” and “DatabaseHelper” files for my application.)
( Copying book.db from Assets…
book.db copied successfully!
Database exists: true
Error opening SQLCipher database!)
Can anyone help me with this issue?
Hi @dev874,
I have a few more questions:
- What version of SQLCipher was used to create the encrypted database?
- You mention the KDF iteration information being included, did you use a non-default value for the KDF iteration length?
- What library are you using to open the SQLCipher database on the Android application?
- What version is that library?
- Can you post some sample code that shows how you are attempting to open the database?
Hello. The things you asked me are :
- DB Browser (SQLCipher) version —> 3.13.1
- PRAGMA cipher_version;------->4.6.1 community
- KDF iterations -----> 64000
- implementation(“net.zetetic:android-database-sqlcipher:4.4.3”)
I will also send the codes.
package com.kitoblarga;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import net.sqlcipher.database.SQLiteDatabase;
public class MainActivity extends AppCompatActivity {
private DatabaseHelper dbHelper;
private TextView tvStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = findViewById(R.id.tvStatus); // 📌 Bind TextView component
dbHelper = new DatabaseHelper(this); // 📌 Create DatabaseHelper object
checkDatabaseConnection(); // 📌 Check database connection
}
// 📌 Check database connection
private void checkDatabaseConnection() {
SQLiteDatabase db = dbHelper.openDatabase();
if (db != null) {
Log.d("MainActivity", "✅ Successfully connected to SQLCipher database!");
tvStatus.setText("✅ Successfully connected to SQLCipher database!"); // 🟢 Display result on screen
db.close();
} else {
Log.e("MainActivity", "❌ Failed to connect to SQLCipher database!");
tvStatus.setText("❌ Failed to connect to SQLCipher database!"); // 🟢 Display error on screen
}
}
}
package com.kitoblarga;
import android.content.Context;
import android.util.Log;
import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "kitob.db";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_PASSWORD = "1"; // 🔐 Password
private final String databasePath;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.databasePath = context.getDatabasePath(DATABASE_NAME).getAbsolutePath();
SQLiteDatabase.loadLibs(context); // 📌 Load SQLCipher library
copyDatabaseFromAssets(context); // 🟢 Copy database from assets if not exists
}
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
// 📌 Check if database exists
private boolean isDatabaseExists() {
File dbFile = new File(databasePath);
boolean exists = dbFile.exists();
Log.d("DatabaseHelper", "🧐 Database exists: " + exists);
return exists;
}
// 📌 Copy kitob.db from assets
private void copyDatabaseFromAssets(Context context) {
try {
File dbFile = new File(databasePath);
if (dbFile.exists()) {
Log.d("DatabaseHelper", "ℹ️ Database already exists, no need to copy.");
return;
}
Log.d("DatabaseHelper", "📂 Copying kitob.db from assets...");
InputStream inputStream = context.getAssets().open(DATABASE_NAME);
OutputStream outputStream = new FileOutputStream(databasePath);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
Log.d("DatabaseHelper", "✅ kitob.db successfully copied!");
} catch (Exception e) {
Log.e("DatabaseHelper", "❌ Error copying kitob.db from assets!", e);
}
}
// 📌 Open the database
public SQLiteDatabase openDatabase() {
if (!isDatabaseExists()) {
Log.e("DatabaseHelper", "❌ kitob.db file does not exist!");
return null;
}
try {
Log.d("DatabaseHelper", "📌 Attempting to open the database...");
SQLiteDatabase db = SQLiteDatabase.openDatabase(databasePath, DATABASE_PASSWORD, null, SQLiteDatabase.OPEN_READWRITE);
// ✅ Check SQLCipher version
Cursor cursor = db.rawQuery("PRAGMA cipher_version;", null);
if (cursor.moveToFirst()) {
Log.d("DatabaseHelper", "✅ SQLCipher version: " + cursor.getString(0));
}
cursor.close();
// ✅ Check KDF iterations (should be 64000)
Cursor kdfCursor = db.rawQuery("PRAGMA kdf_iter;", null);
if (kdfCursor.moveToFirst()) {
int kdfValue = kdfCursor.getInt(0);
Log.d("DatabaseHelper", "✅ KDF iterations: " + kdfValue);
if (kdfValue != 64000) {
Log.e("DatabaseHelper", "⚠️ Incorrect KDF iterations! Expected: 64000, Found: " + kdfValue);
}
}
kdfCursor.close();
return db;
} catch (Exception e) {
Log.e("DatabaseHelper", "❌ Error opening SQLCipher database!", e);
return null;
}
}
}
Is there a clear rule about which DB Browser (SQLCipher) version can work with which net.zetetic:android-database-sqlcipher:4.4.3 version? Thanks in advance
Hi @dev874,
Since you are using a non-default KDF iteration length, you will need to supply that value when accessing the database. Your sample code appears to be a SQLiteOpenHelper
derivative in which case you don’t need define an openDatabase()
function as the class already provides a method getWritableDatabase(...)
. The constructor will accept a SQLiteDatabaseHook
which will allow you to specify your custom KDF iteration length in a postKey
operation using the PRAGMA kdf_iter
command [1].
Also, you should be aware the the android-database-sqlcipher
library has been deprecated, which has been replaced by sqlcipher-android
a long-term supported replacement library.
Zetetic does not develop the DB Browser application, although it should work on both SQLCipher 3.x and 4.x database files currently.
Hello. Thanks for the answer. Can you show me an example of a program that opens an encrypted .db file created in DB Browser (SQLCipher) in an Android application? For example, from GitHub or similar sites. Or even the program code will work.
Hi @dev874,
Here is an example [1] of using the SQLiteDatabaseHook
to open an older SQLCipher database file which must be migrated to the new file format using the PRAGMA cipher_migrate
command. In your case, you will want to use the postKey
event to specify your custom KDF iteration length.