package com.longnh15.heimdall.common import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import java.security.KeyStore import javax.crypto.Cipher import javax.crypto.KeyGenerator import javax.crypto.SecretKey import javax.crypto.spec.GCMParameterSpec import kotlin.io.encoding.Base64 object TokenCrypto { private const val PROVIDER = "AndroidKeyStore" private const val ALIAS = "auth_token_key" private const val TRANSFORMATION = "AES/GCM/NoPadding" private const val IV_SIZE = 12 private const val TAG_BITS = 128 private val keystore = KeyStore.getInstance(PROVIDER).apply { load(null) } private fun getOrCreateKey(): SecretKey { (keystore.getEntry(ALIAS, null) as? KeyStore.SecretKeyEntry)?.let { return it.secretKey } val keyGen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, PROVIDER) keyGen.init( KeyGenParameterSpec.Builder( ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT ) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) .build() ) return keyGen.generateKey() } fun encrypt(plainText: String): String { val cipher = Cipher.getInstance(TRANSFORMATION) cipher.init(Cipher.ENCRYPT_MODE, getOrCreateKey()) val iv = cipher.iv // 12-byte IV, generated by Keystore val cipherText = cipher.doFinal(plainText.toByteArray(Charsets.UTF_8)) return Base64.encode(iv + cipherText) // prepend IV } fun decrypt(encoded: String): String { val combined = Base64.decode(encoded) val iv = combined.copyOfRange(0, IV_SIZE) val cipherText = combined.copyOfRange(IV_SIZE, combined.size) val cipher = Cipher.getInstance(TRANSFORMATION) cipher.init(Cipher.DECRYPT_MODE, getOrCreateKey(), GCMParameterSpec(TAG_BITS, iv)) return cipher.doFinal(cipherText).toString(Charsets.UTF_8) } }