Simple keep your password in app level build.graldle
file like:
android {
compileSdkVersion 30
buildToolsVersion = '29.0.3'
defaultConfig {
buildConfigField 'String', 'prefPass', '"YourPasswordHere"'
// rest of the code goes here..
And use it like:
BuildConfig.prefPass
As build.gradle
file is not part of an APK source code, so no one can steal it.
Why are you saving your password in shared preferences? why can't you use it directly from BuildConfig?. If you really want to save it then encrypt and save it using this class:
import android.util.Log
import java.security.MessageDigest
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object CryptographyUtil {
private const val TAG: String = "CryptographyUtil"
private const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5PADDING"
private const val KEY_ALGORITHM = "AES"
// https://developer.android.com/guide/topics/security/cryptography#encrypt-message
fun encryptData(key: String, data: String): String {
try {
val secretKeySpec = generateSecretKey(key)
val cipher = Cipher.getInstance(CIPHER_TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, getIVSpecification(key))
val encryptValue = cipher.doFinal(data.toByteArray())
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Base64.getEncoder().encodeToString(encryptValue)
} else {
android.util.Base64.encodeToString(encryptValue, android.util.Base64.DEFAULT)
}
} catch (e: Exception) {
Log.d(TAG, "Error while encrypt ${e.message}")
}
return data
}
fun decryptData(key: String, encryptedData: String): String {
try {
val secretKeySpec = generateSecretKey(key)
val cipher = Cipher.getInstance(CIPHER_TRANSFORMATION)
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, getIVSpecification(key))
val decodeValue = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Base64.getDecoder().decode(encryptedData)
} else {
android.util.Base64.decode(encryptedData, android.util.Base64.DEFAULT)
}
return String(cipher.doFinal(decodeValue))
} catch (e: Exception) {
Log.d(TAG, "Error while decrypt ${e.message}")
}
return encryptedData
}
private fun generateSecretKey(key: String): SecretKeySpec {
val messageDigest = MessageDigest.getInstance("SHA-256")
val bytes = key.toByteArray(Charsets.UTF_8)
messageDigest.update(bytes, 0, bytes.size)
return SecretKeySpec(messageDigest.digest(), KEY_ALGORITHM)
}
private fun getIVSpecification(key: String): IvParameterSpec {
// concat string so that key has always size greater than 16 bytes & we can
// get first 16 character for generating IV specification.
// As documentation suggest that IV specification key can't be less or greater than 16 bytes
val concatKey = key + key
return IvParameterSpec(concatKey.substring(0, 16).toByteArray())
}
}
Use the above class in your EncryptedSharedPreferences
class:
val encryptData = encryptData(BuildConfig.prefPass,
BuildConfig.prefPass)
encryptedSharedPreferences.edit().apply {
putString("MY_KEY", encryptData)
}.apply()
Note: To make this work you need to enable minify to hide the BuildConfig
file.
buildTypes {
release {
debuggable false
minifyEnabled true
shrinkResources true
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
shrinkResources true
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Check this answer here
An additional feature of the minifying step is the inlining of constants. This would explain why the BuildConfig disappears, and yet the values still exist where needed. Once the values get inlined, there are no more references to the BuildConfig class and the minifier can remove it entirely
The encryption part is to just prevent Key from being read out of the app, by definition, that's impossible. If the key can be used by your app, it can be used by a modified version of your app that dumps the key to LogCat or something. Tools like ProGuard, DexGuard, and kin make it a bit more difficult to access, but they cannot prevent it. The only way to prevent the key from being accessed is to not have it in the app in the first place.
Ref: https://stackoverflow.com/a/30238695/2462531