-
Notifications
You must be signed in to change notification settings - Fork 991
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MBL-1269: Encrypt/decript token (#1983)
- Loading branch information
Showing
7 changed files
with
225 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
app/src/main/java/com/kickstarter/libs/keystore/EncryptionEngine.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.kickstarter.libs.keystore | ||
|
||
import android.content.Context | ||
import android.content.SharedPreferences | ||
import android.security.keystore.KeyProperties | ||
import android.security.keystore.KeyProtection | ||
import com.kickstarter.libs.featureflag.FeatureFlagClientType | ||
import com.kickstarter.libs.featureflag.FlagKey | ||
import com.kickstarter.libs.preferences.StringPreferenceType | ||
import com.kickstarter.libs.utils.extensions.decrypt | ||
import com.kickstarter.libs.utils.extensions.encrypt | ||
import com.kickstarter.libs.utils.extensions.isKSApplication | ||
import timber.log.Timber | ||
import java.security.Key | ||
import java.security.KeyStore | ||
import javax.crypto.KeyGenerator | ||
import javax.crypto.SecretKey | ||
|
||
interface KSKeyStore { | ||
var ksKeyStore: KeyStore? | ||
fun getSecretKey(keyAlias: String): Key? { | ||
return ksKeyStore?.getKey(keyAlias, null) | ||
} | ||
|
||
fun generateSecretKey(keyAlias: String) { | ||
ksKeyStore?.let { | ||
val keyGen = KeyGenerator.getInstance("AES") | ||
keyGen.init(256) | ||
val secretKey: SecretKey = keyGen.generateKey() | ||
|
||
// - Set duration/rotation for the key on the keyStorage | ||
// val start: Calendar = Calendar.getInstance() | ||
// val end: Calendar = Calendar.getInstance() | ||
// end.add(Calendar.YEAR, 2) | ||
|
||
val entry = KeyStore.SecretKeyEntry(secretKey) | ||
val protectionParameter = | ||
KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) | ||
// .setKeyValidityStart(start.time) | ||
// .setKeyValidityEnd(end.time) | ||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC) | ||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) | ||
.build() | ||
|
||
it.setEntry(keyAlias, entry, protectionParameter) | ||
} | ||
} | ||
} | ||
class EncryptionEngine( | ||
private val sharedPreferences: SharedPreferences, | ||
private val keyAlias: String, | ||
private val defaultValue: String = "", | ||
private val context: Context, | ||
private val featureFlagClient: FeatureFlagClientType | ||
) : StringPreferenceType { | ||
|
||
// - Avoid instantiating KeyStore on test Applications | ||
var ksKeyStore = object : KSKeyStore { | ||
override var ksKeyStore: KeyStore? = | ||
if (context.isKSApplication()) KeyStore.getInstance("AndroidKeyStore").apply { load(null) } | ||
else null | ||
} | ||
|
||
// - Overload to be able to use kotlin named parameters from JAVA code | ||
constructor( | ||
sharedPreferences: SharedPreferences, | ||
accessToken: String, | ||
context: Context, | ||
featureFlagClient: FeatureFlagClientType | ||
) : this(sharedPreferences = sharedPreferences, keyAlias = accessToken, context = context, featureFlagClient = featureFlagClient) { | ||
Timber.d("$this :Overloaded constructor") | ||
|
||
ksKeyStore.generateSecretKey(keyAlias = keyAlias) | ||
} | ||
|
||
override val isSet: Boolean | ||
get() = sharedPreferences.contains(keyAlias) | ||
|
||
override fun get(): String { | ||
return if (isSet) { | ||
if (featureFlagClient.getBoolean(FlagKey.ANDROID_ENCRYPT)) { | ||
val b64 = sharedPreferences.getString(keyAlias, defaultValue) ?: defaultValue | ||
val secretKey = ksKeyStore.getSecretKey(keyAlias) | ||
return b64.decrypt(secretKey) ?: defaultValue | ||
} else sharedPreferences.getString(keyAlias, defaultValue) ?: defaultValue | ||
} else "" | ||
} | ||
override fun set(value: String?) { | ||
value?.let { | ||
if (featureFlagClient.getBoolean(FlagKey = FlagKey.ANDROID_ENCRYPT)) { | ||
val secretKey = ksKeyStore.getSecretKey(keyAlias) | ||
val encryptedData = value.encrypt(secretKey = secretKey) | ||
sharedPreferences.edit().putString(keyAlias, encryptedData).apply() | ||
} else sharedPreferences.edit().putString(keyAlias, value).apply() | ||
} | ||
} | ||
override fun delete() { | ||
sharedPreferences.edit().remove(keyAlias).apply() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
app/src/test/java/com/kickstarter/libs/preferences/EncryptionEngineTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.kickstarter.libs.preferences | ||
|
||
import android.content.Context | ||
import com.kickstarter.KSRobolectricTestCase | ||
import com.kickstarter.libs.Build | ||
import com.kickstarter.libs.MockSharedPreferences | ||
import com.kickstarter.libs.keystore.EncryptionEngine | ||
import com.kickstarter.libs.keystore.KSKeyStore | ||
import com.kickstarter.mock.MockFeatureFlagClient | ||
import org.junit.Test | ||
import java.security.Key | ||
import java.security.KeyStore | ||
import javax.crypto.spec.SecretKeySpec | ||
|
||
class EncryptionEngineTest : KSRobolectricTestCase() { | ||
|
||
lateinit var build: Build | ||
lateinit var context: Context | ||
|
||
override fun setUp() { | ||
super.setUp() | ||
build = requireNotNull(environment().build()) | ||
context = application() | ||
} | ||
@Test | ||
fun testEncryptDecrypt() { | ||
|
||
val mockKSKeyStore = object : KSKeyStore { | ||
override var ksKeyStore: KeyStore? = null | ||
|
||
override fun getSecretKey(keyAlias: String): Key? { | ||
val key = "aesEncryptionKey" | ||
return SecretKeySpec(key.toByteArray(), "AES") | ||
} | ||
} | ||
val mockffClient = MockFeatureFlagClient() | ||
val engine = EncryptionEngine( | ||
sharedPreferences = MockSharedPreferences(), | ||
"Alias", | ||
context, | ||
mockffClient, | ||
) | ||
|
||
engine.ksKeyStore = mockKSKeyStore | ||
|
||
val textForEncryption = "This my text that will be encrypted!" | ||
|
||
engine.set(textForEncryption) | ||
val decrypted = engine.get() | ||
|
||
assertEquals(textForEncryption, decrypted) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters