Created
February 19, 2020 17:54
-
-
Save isaidamier/49edae4c2fd2385e707606ec73eb53de to your computer and use it in GitHub Desktop.
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
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