d4nsupperman

SecureStorageManager.kt

Dec 13th, 2024
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 6.38 KB | Source Code | 0 0
  1. import android.content.Context
  2. import android.os.Build
  3. import android.util.Base64
  4. import androidx.annotation.RequiresApi
  5. import java.security.SecureRandom
  6. import javax.crypto.Cipher
  7. import javax.crypto.KeyGenerator
  8. import javax.crypto.Mac
  9. import javax.crypto.SecretKey
  10. import javax.crypto.spec.GCMParameterSpec
  11. import javax.crypto.spec.IvParameterSpec
  12. import javax.crypto.spec.SecretKeySpec
  13.  
  14. class SecureStorageManager(context: Context) {
  15.  
  16.     companion object {
  17.         private const val AES_TRANSFORMATION = "AES/GCM/NoPadding"
  18.         private const val AES_LEGACY_TRANSFORMATION = "AES/CBC/PKCS5Padding"
  19.         private const val HMAC_ALGORITHM = "HmacSHA256"
  20.         private const val PREFS_NAME = "secure_storage"
  21.         private const val ENCRYPTED_KEY_ALIAS = "encrypted_key"
  22.     }
  23.  
  24.     internal val sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
  25.  
  26.     // Save encrypted data
  27.     fun encryptAndSave(key: String, data: String) {
  28.         val (iv, encryptedData) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  29.             encryptWithKeystore(data)
  30.         } else {
  31.             encryptWithAES(data)
  32.         }
  33.  
  34.         val hmac = generateHMAC(encryptedData)
  35.  
  36.         sharedPreferences.edit()
  37.             .putString("$key.data", Base64.encodeToString(encryptedData, Base64.DEFAULT))
  38.             .putString("$key.iv", Base64.encodeToString(iv, Base64.DEFAULT))
  39.             .putString("$key.hmac", Base64.encodeToString(hmac, Base64.DEFAULT))
  40.             .apply()
  41.     }
  42.  
  43.     // Retrieve and decrypt data
  44.     fun decrypt(key: String): String? {
  45.         val encryptedData = sharedPreferences.getString("$key.data", null)?.let {
  46.             Base64.decode(it, Base64.DEFAULT)
  47.         } ?: return null
  48.  
  49.         val iv = sharedPreferences.getString("$key.iv", null)?.let {
  50.             Base64.decode(it, Base64.DEFAULT)
  51.         } ?: return null
  52.  
  53.         val storedHmac = sharedPreferences.getString("$key.hmac", null)?.let {
  54.             Base64.decode(it, Base64.DEFAULT)
  55.         } ?: return null
  56.  
  57.         if (!verifyHMAC(encryptedData, storedHmac)) {
  58.             throw SecurityException("HMAC verification failed")
  59.         }
  60.  
  61.         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  62.             decryptWithKeystore(encryptedData, iv)
  63.         } else {
  64.             decryptWithAES(encryptedData, iv)
  65.         }
  66.     }
  67.  
  68.     // Encrypt with Keystore for Android >= 23
  69.     @RequiresApi(Build.VERSION_CODES.M)
  70.     private fun encryptWithKeystore(data: String): Pair<ByteArray, ByteArray> {
  71.         val cipher = Cipher.getInstance(AES_TRANSFORMATION)
  72.         val key = getOrCreateKeyForKeystore()
  73.         cipher.init(Cipher.ENCRYPT_MODE, key)
  74.         val iv = cipher.iv
  75.         val encryptedData = cipher.doFinal(data.toByteArray())
  76.         return Pair(iv, encryptedData)
  77.     }
  78.  
  79.     // Decrypt with Keystore for Android >= 23
  80.     @RequiresApi(Build.VERSION_CODES.M)
  81.     private fun decryptWithKeystore(encryptedData: ByteArray, iv: ByteArray): String {
  82.         val cipher = Cipher.getInstance(AES_TRANSFORMATION)
  83.         val key = getOrCreateKeyForKeystore()
  84.         cipher.init(Cipher.DECRYPT_MODE, key, GCMParameterSpec(128, iv))
  85.         val decryptedBytes = cipher.doFinal(encryptedData)
  86.         return String(decryptedBytes)
  87.     }
  88.  
  89.     // Get or create a Keystore key
  90.     @RequiresApi(Build.VERSION_CODES.M)
  91.     private fun getOrCreateKeyForKeystore(): SecretKey {
  92.         val keyStore = java.security.KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
  93.         val alias = "SecureStorageKey"
  94.         if (!keyStore.containsAlias(alias)) {
  95.             val keyGenerator = KeyGenerator.getInstance("AES", "AndroidKeyStore")
  96.             keyGenerator.init(
  97.                 android.security.keystore.KeyGenParameterSpec.Builder(
  98.                     alias,
  99.                     android.security.keystore.KeyProperties.PURPOSE_ENCRYPT or android.security.keystore.KeyProperties.PURPOSE_DECRYPT
  100.                 )
  101.                     .setBlockModes(android.security.keystore.KeyProperties.BLOCK_MODE_GCM)
  102.                     .setEncryptionPaddings(android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE)
  103.                     .build()
  104.             )
  105.             keyGenerator.generateKey()
  106.         }
  107.         return keyStore.getKey(alias, null) as SecretKey
  108.     }
  109.  
  110.     // Encrypt with AES for Android < 23
  111.     private fun encryptWithAES(data: String): Pair<ByteArray, ByteArray> {
  112.         val cipher = Cipher.getInstance(AES_LEGACY_TRANSFORMATION)
  113.         val iv = ByteArray(16).apply { SecureRandom().nextBytes(this) }
  114.         val key = getLegacyAESKey()
  115.         cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
  116.         val encryptedData = cipher.doFinal(data.toByteArray())
  117.         return Pair(iv, encryptedData)
  118.     }
  119.  
  120.     // Decrypt with AES for Android < 23
  121.     private fun decryptWithAES(encryptedData: ByteArray, iv: ByteArray): String {
  122.         val cipher = Cipher.getInstance(AES_LEGACY_TRANSFORMATION)
  123.         val key = getLegacyAESKey()
  124.         cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))
  125.         val decryptedBytes = cipher.doFinal(encryptedData)
  126.         return String(decryptedBytes)
  127.     }
  128.  
  129.     // Generate or retrieve legacy AES key for Android < 23
  130.     private fun getLegacyAESKey(): SecretKey {
  131.         val existingKey = sharedPreferences.getString(ENCRYPTED_KEY_ALIAS, null)
  132.         return if (existingKey != null) {
  133.             val decodedKey = Base64.decode(existingKey, Base64.DEFAULT)
  134.             SecretKeySpec(decodedKey, "AES")
  135.         } else {
  136.             val keyGenerator = KeyGenerator.getInstance("AES")
  137.             keyGenerator.init(256)
  138.             val newKey = keyGenerator.generateKey()
  139.             sharedPreferences.edit()
  140.                 .putString(ENCRYPTED_KEY_ALIAS, Base64.encodeToString(newKey.encoded, Base64.DEFAULT))
  141.                 .apply()
  142.             newKey
  143.         }
  144.     }
  145.  
  146.     // Generate HMAC for data
  147.     private fun generateHMAC(data: ByteArray): ByteArray {
  148.         val key = getLegacyAESKey()
  149.         val mac = Mac.getInstance(HMAC_ALGORITHM)
  150.         mac.init(SecretKeySpec(key.encoded, HMAC_ALGORITHM))
  151.         return mac.doFinal(data)
  152.     }
  153.  
  154.     // Verify HMAC for data
  155.     private fun verifyHMAC(data: ByteArray, hmac: ByteArray): Boolean {
  156.         val calculatedHMAC = generateHMAC(data)
  157.         return calculatedHMAC.contentEquals(hmac)
  158.     }
  159. }
  160.  
Tags: security
Add Comment
Please, Sign In to add comment