Skip to content

Instantly share code, notes, and snippets.

@isaidamier
Created February 19, 2020 17:54
Show Gist options
  • Save isaidamier/49edae4c2fd2385e707606ec73eb53de to your computer and use it in GitHub Desktop.
Save isaidamier/49edae4c2fd2385e707606ec73eb53de to your computer and use it in GitHub Desktop.
interface CryptographyManager {
/**
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
* with the key. The secret key uses [ENCRYPT_MODE][Cipher.ENCRYPT_MODE] is used.
*/
fun getInitializedCipherForEncryption(keyName: String): Cipher
/**
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
* with the key. The secret key uses [DECRYPT_MODE][Cipher.DECRYPT_MODE] is used.
*/
fun getInitializedCipherForDecryption(keyName: String, initializationVector: ByteArray): Cipher
/**
* The Cipher created with [getInitializedCipherForEncryption] is used here
*/
fun encryptData(plaintext: String, cipher: Cipher): EncryptedData
/**
* The Cipher created with [getInitializedCipherForDecryption] is used here
*/
fun decryptData(ciphertext: ByteArray, cipher: Cipher): String
}
fun CryptographyManager(): CryptographyManager = CryptographyManagerImpl()
data class EncryptedData(val ciphertext: ByteArray, val initializationVector: ByteArray)
private class CryptographyManagerImpl : CryptographyManager {
private val KEY_SIZE: Int = 256
val ANDROID_KEYSTORE = "AndroidKeyStore"
private val ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM
private val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE
private val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
override fun getInitializedCipherForEncryption(keyName: String): Cipher {
val cipher = getCipher()
val secretKey = getOrCreateSecretKey(keyName)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
return cipher
}
override fun getInitializedCipherForDecryption(keyName: String, initializationVector: ByteArray): Cipher {
val cipher = getCipher()
val secretKey = getOrCreateSecretKey(keyName)
cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, initializationVector))
return cipher
}
override fun encryptData(plaintext: String, cipher: Cipher): EncryptedData {
val ciphertext = cipher.doFinal(plaintext.toByteArray(Charset.forName("UTF-8")))
return EncryptedData(ciphertext,cipher.iv)
}
override fun decryptData(ciphertext: ByteArray, cipher: Cipher): String {
val plaintext = cipher.doFinal(ciphertext)
return String(plaintext, Charset.forName("UTF-8"))
}
private fun getCipher(): Cipher {
val transformation = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING"
return Cipher.getInstance(transformation)
}
private fun getOrCreateSecretKey(keyName: String): SecretKey {
// If Secretkey was previously created for that keyName, then grab and return it.
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
keyStore.load(null) // Keystore must be loaded before it can be accessed
keyStore.getKey(keyName, null)?.let { return it as SecretKey }
// if you reach here, then a new SecretKey must be generated for that keyName
val paramsBuilder = KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
paramsBuilder.apply {
setBlockModes(ENCRYPTION_BLOCK_MODE)
setEncryptionPaddings(ENCRYPTION_PADDING)
setKeySize(KEY_SIZE)
setUserAuthenticationRequired(true)
}
val keyGenParams = paramsBuilder.build()
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
ANDROID_KEYSTORE)
keyGenerator.init(keyGenParams)
return keyGenerator.generateKey()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment