Secure Data Storage in Android

Secure Data Storage in Android

Android provides multiple options for data storage, each with different security characteristics. Choosing the right storage method and implementing it securely is critical.

SharedPreferences Security:

// Using EncryptedSharedPreferences for sensitive data
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import android.content.Context
import android.content.SharedPreferences

class SecurePreferencesManager(context: Context) {
    
    private val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
    
    private val encryptedPrefs: SharedPreferences = EncryptedSharedPreferences.create(
        "secure_prefs",
        masterKeyAlias,
        context,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
    
    fun saveSecureToken(token: String) {
        encryptedPrefs.edit().putString("auth_token", token).apply()
    }
    
    fun getSecureToken(): String? {
        return encryptedPrefs.getString("auth_token", null)
    }
    
    fun clearSecureData() {
        encryptedPrefs.edit().clear().apply()
    }
}

Database Encryption with SQLCipher:

// Implementing encrypted database
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SQLiteOpenHelper
import android.content.Context
import android.content.ContentValues

class SecureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
    
    companion object {
        private const val DB_NAME = "secure.db"
        private const val DB_VERSION = 1
        private const val TABLE_SECRETS = "secrets"
    }
    
    init {
        // Initialize SQLCipher
        SQLiteDatabase.loadLibs(context)
    }
    
    override fun onCreate(db: SQLiteDatabase) {
        val createTable = """
            CREATE TABLE $TABLE_SECRETS (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                key TEXT NOT NULL,
                value TEXT NOT NULL,
                created_at INTEGER NOT NULL
            )
        """.trimIndent()
        
        db.execSQL(createTable)
    }
    
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        // Handle database upgrades
    }
    
    fun saveSecret(key: String, value: String, password: CharArray) {
        val db = getWritableDatabase(password)
        
        val values = ContentValues().apply {
            put("key", key)
            put("value", value)
            put("created_at", System.currentTimeMillis())
        }
        
        db.insert(TABLE_SECRETS, null, values)
        db.close()
        
        // Clear password from memory
        password.fill(' ')
    }
}

File Encryption:

// Secure file storage implementation
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec

class FileEncryptionManager {
    
    private val keyAlias = "FileEncryptionKey"
    private val androidKeyStore = "AndroidKeyStore"
    private val transformation = "AES/GCM/NoPadding"
    
    init {
        generateKey()
    }
    
    private fun generateKey() {
        val keyStore = KeyStore.getInstance(androidKeyStore)
        keyStore.load(null)
        
        if (!keyStore.containsAlias(keyAlias)) {
            val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, androidKeyStore)
            
            val spec = KeyGenParameterSpec.Builder(
                keyAlias,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setRandomizedEncryptionRequired(true)
                .setUserAuthenticationRequired(true)
                .setUserAuthenticationValidityDurationSeconds(120)
                .build()
            
            keyGenerator.init(spec)
            keyGenerator.generateKey()
        }
    }
    
    fun encryptFile(inputFile: File, outputFile: File) {
        val keyStore = KeyStore.getInstance(androidKeyStore)
        keyStore.load(null)
        
        val secretKey = keyStore.getKey(keyAlias, null) as SecretKey
        val cipher = Cipher.getInstance(transformation)
        cipher.init(Cipher.ENCRYPT_MODE, secretKey)
        
        val iv = cipher.iv
        
        FileInputStream(inputFile).use { input ->
            FileOutputStream(outputFile).use { output ->
                // Write IV to beginning of file
                output.write(iv)
                
                // Encrypt file contents
                val buffer = ByteArray(1024)
                var bytesRead: Int
                
                while (input.read(buffer).also { bytesRead = it } != -1) {
                    val encrypted = cipher.update(buffer, 0, bytesRead)
                    if (encrypted != null) {
                        output.write(encrypted)
                    }
                }
                
                val finalBytes = cipher.doFinal()
                if (finalBytes != null) {
                    output.write(finalBytes)
                }
            }
        }
    }
}