# Encrypted Databases

> Encrypting local databases on mobile with SQLCipher, Room + EncryptedFile, or Realm encryption. Use when persisting structured sensitive data on device.

- Skill: `almasumdev/encrypted-databases` (Agent Skill)
- Install (CLI): `npx skillmds@latest add almasumdev/encrypted-databases`
- Raw SKILL.md: https://api.skillmd.com/api/skills/almasumdev/encrypted-databases/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: almasumdev (https://skillmd.com/u/almasumdev)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/almasumdev/encrypted-databases

---


# Encrypted Databases on Mobile

## Instructions

Plain SQLite and plain Realm files are readable by anyone with filesystem access — including forensic tools and, on rooted/jailbroken devices, other apps. Encrypt at rest for anything sensitive.

### 1. When to Encrypt

Encrypt when the DB contains:
- Session tokens, refresh tokens, or API credentials.
- PII (email, phone, address, national IDs).
- Financial / health records.
- User-generated content marked private.

Do **not** encrypt purely public / cached content; it wastes CPU and complicates debugging.

### 2. Key Management (The Actual Hard Part)

- Generate a random 256-bit key once per install.
- Wrap it with a **keystore-backed** key (Android Keystore / iOS Keychain).
- Store the wrapped blob in `EncryptedSharedPreferences` / Keychain.
- **Never** derive the DB key from a constant string or from `android_id`.

### 3. Android: Room + SQLCipher

```kotlin
// build.gradle.kts
// implementation("net.zetetic:sqlcipher-android:4.6.1")
// implementation("androidx.sqlite:sqlite:2.4.0")

val passphrase: ByteArray = KeyStoreHelper.loadOrCreateDbKey() // 32 bytes
val factory = SupportOpenHelperFactory(passphrase, null, false)

val db = Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db")
    .openHelperFactory(factory)
    .build()

// Zero the passphrase as soon as Room has it.
passphrase.fill(0)
```

### 4. Android: Room + `EncryptedFile` (Alternative)

For small stores where full-text queries are not required, use Jetpack `EncryptedFile`:

```kotlin
val file = EncryptedFile.Builder(
    ctx, File(ctx.filesDir, "notes.bin"), masterKey,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB,
).build()

file.openFileOutput().use { it.write(payload) }
```

This is simpler than SQLCipher but loses SQL query capabilities.

### 5. iOS: SQLCipher via GRDB

```swift
var config = Configuration()
config.prepareDatabase { db in
    let key = try KeychainHelper.loadOrCreateDbKey() // 32-byte Data
    try db.usePassphrase(key)
}
let dbQueue = try DatabaseQueue(path: path, configuration: config)
```

For Core Data, wrap the store with `NSPersistentStoreFileProtectionKey: FileProtectionType.complete` — NOT equivalent to SQLCipher, but acceptable when the device is locked.

### 6. Realm

```kotlin
val config = RealmConfiguration.Builder(schema = setOf(User::class))
    .name("user.realm")
    .encryptionKey(KeyStoreHelper.loadOrCreateRealmKey()) // 64 bytes
    .build()
val realm = Realm.open(config)
```

Realm requires a **64-byte** key. Losing it means the DB is unrecoverable — plan for re-sync from server on key loss.

### 7. Migration From Plaintext

If you inherit a plaintext DB:

1. Open it plaintext.
2. Attach a new encrypted DB with the new key.
3. `INSERT INTO encrypted.table SELECT * FROM plain.table;` for each table.
4. Close, delete the plaintext file, rename encrypted → canonical path.
5. Run on a background thread with progress UI; this can take minutes on large DBs.

### 8. Debug vs Release

- In debug builds you may want to disable encryption to allow inspection with DB Browser / Stetho. Gate this on `BuildConfig.DEBUG` / `#if DEBUG` so it cannot ship.
- Never commit a debug key that also unlocks production dumps.

## Checklist

- [ ] The DB key is random (not derived from a constant or device ID).
- [ ] The DB key is wrapped by Keystore / Keychain and never stored plaintext on disk.
- [ ] SQLCipher / Realm encryption is enabled on any DB containing PII or credentials.
- [ ] A documented migration exists for moving users from plaintext to encrypted DBs.
- [ ] Key-loss UX is handled (re-sync or re-login) rather than silent data corruption.
- [ ] Debug-only relaxations cannot ship to production.

