Encrypt Data in Android Room Data Base

Encryption

SQLCipher

Implementation

Dependency

implementation "net.zetetic:android-database-sqlcipher:4.5.0"
implementation "androidx.security:security-crypto:1.1.0-alpha03"

Room integration

Room.databaseBuilder(
context,
DataBase::class.java,
DATABASE_NAME
).build()
Room.databaseBuilder(
context,
DataBase::class.java,
DATABASE_NAME
).openHelperFactory(SupportFactory(securePassphrase)).build()
"secureKey".toByteArray()
Base64.decode("secureKey", Base64.DEFAULT)
private fun generatePassphrase(): ByteArray {
// Generate a 256-bit key
val outputKeyLength = keyLength

val secureRandom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
SecureRandom.getInstanceStrong()
} else {
// Do *not* seed secureRandom! Automatically seeded from system entropy.
SecureRandom()
}
val keyGenerator: KeyGenerator = KeyGenerator.getInstance(algorithm)
keyGenerator.init(outputKeyLength, secureRandom)

var tempArray = keyGenerator.generateKey().encoded

// filter out zero byte values, as SQLCipher does not like them
while (tempArray.contains(0)) {
tempArray = keyGenerator.generateKey().encoded
}

return tempArray
}
fun getPassphraseFromSecureFile(): ByteArray {
val file = File(context.filesDir, fileName)
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val encryptedFile = EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

return if (file.exists()) {
encryptedFile.openFileInput().use { it.readBytes() }
} else {
generatePassphrase().also { passphrase ->
encryptedFile.openFileOutput().use { it.write(passphrase) }
}
}
}

Migration

import android.content.Context
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import net.sqlcipher.database.SQLiteDatabase

object SQLCipherUtils {

/**
* Replaces this database with a version encrypted with the supplied
* passphrase, deleting the original. Do not call this while the database
* is open, which includes during any Room migrations.
*
* The passphrase is untouched in this call. If you are going to turn around
* and use it with SafeHelperFactory.fromUser(), fromUser() will clear the
* passphrase. If not, please set all bytes of the passphrase to 0 or something
* to clear out the passphrase.
*
*
@param ctxt a Context
*
@param originalFile a File pointing to the database
*
@param passphrase the passphrase from the user
*
@throws IOException
*/
@Throws(IOException::class)
fun encrypt(ctxt: Context, originalFile: File, passphrase: ByteArray?) {
SQLiteDatabase.loadLibs(ctxt)
if (originalFile.exists()) {
val newFile = File.createTempFile(
"sqlcipherutils", "tmp",
ctxt.cacheDir
)
var db = SQLiteDatabase.openDatabase(
originalFile.absolutePath,
"", null, SQLiteDatabase.OPEN_READWRITE
)

val version = db.version
db.close()
db = SQLiteDatabase.openDatabase(
newFile.absolutePath, passphrase,
null, SQLiteDatabase.OPEN_READWRITE, null, null
)
val st = db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''")
st.bindString(1, originalFile.absolutePath)
st.execute()
db.rawExecSQL("SELECT sqlcipher_export('main', 'plaintext')")
db.rawExecSQL("DETACH DATABASE plaintext")
db.version = version
st.close()
db.close()
originalFile.delete()
newFile.renameTo(originalFile)
} else {
throw FileNotFoundException(originalFile.absolutePath + " not found")
}
}
}
val state = SQLCipherUtils.getDatabaseState(
context,
DATABASE_NAME
)

val key = getPassphraseFromSecureFile()

if (state == SQLCipherUtils.State.UNENCRYPTED) {

SQLCipherUtils.encrypt(
context,
DATABASE_NAME,
key
)
}
return Room.databaseBuilder(context, DataBase::class.java, DATABASE_NAME)
.fallbackToDestructiveMigration()
.openHelperFactory(KeyManagerRepository(context).getCypherFactory())
.build()

--

--

--

Android Developer from El Salvador

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Deep dive into Coroutine Flow 1

Build your own Android AR Core app with ConfigWise SDK

How to Create Animations and Transitions for your Android App

Review: flutter_launcher_icons. Everything you need to create an application icon in Flutter

Launch ThinkDiag Full All Manufacturer software and All Reset software

Setting up Android Development workbench using Flutter and Android Studio on Ubuntu 16.04 Desktop

Make Your Own Custom Android Launcher using Flutter

How to factorize layer code in Android development

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Elias Eguizabal

Elias Eguizabal

Android Developer from El Salvador

More from Medium

Android Development — MockK 101 (Part 1)

Espresso UI Testing for Intents in Android

Top 10 iPhone applications when traveling in van — App iOS News

Working with the WorkManager — Part 2