Skip to content

Instantly share code, notes, and snippets.

@shishirthedev
Created July 14, 2024 19:07
Show Gist options
  • Select an option

  • Save shishirthedev/61e342b1a39c7ce7b988b25f8511ddd9 to your computer and use it in GitHub Desktop.

Select an option

Save shishirthedev/61e342b1a39c7ce7b988b25f8511ddd9 to your computer and use it in GitHub Desktop.
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
import java.nio.charset.StandardCharsets
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.PrivateKey
import java.security.PublicKey
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
/**
* @Created_by: Shishir
* @Created_on: 15,July,2024
*/
object CryptoUtils {
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
private const val AES_KEY_ALIAS = "MyAESKeyAlias"
private const val AES_MODE = "AES/GCM/NoPadding"
private const val GCM_TAG_LENGTH = 128
private const val IV_SEPARATOR = "]"
fun generateAESKey(): SecretKey {
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
keyStore.load(null)
if (!keyStore.containsAlias(AES_KEY_ALIAS)) {
val keyGenerator = KeyGenerator.getInstance(AES_MODE, ANDROID_KEYSTORE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
keyGenerator.init(
KeyGenParameterSpec.Builder(
AES_KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
)
} else {
// For API level 21 and 22, use default initialization
keyGenerator.init(256) // Use 256-bit key size
}
return keyGenerator.generateKey()
} else {
// Key already exists, retrieve it from the Keystore
return keyStore.getKey(AES_KEY_ALIAS, null) as SecretKey
}
}
fun encrypt(
plaintext: String,
secretKey: SecretKey
): String {
return try {
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
// iv base64 string
val ivBase64String = Base64.encodeToString(cipher.iv, Base64.DEFAULT).trim()
// cipher base64 string
val ciphertext = cipher.doFinal(plaintext.toByteArray(StandardCharsets.UTF_8))
val ciphertextBase64String = Base64.encodeToString(ciphertext, Base64.DEFAULT).trim()
// Combine IV and ciphertext for storage or transmission
return ivBase64String + IV_SEPARATOR + ciphertextBase64String
} catch (e: Exception) {
e.printStackTrace()
""
}
}
// Decrypt ciphertext using AES decryption
fun decrypt(ciphertext: String, secretKey: SecretKey): String {
return try {
val parts = ciphertext.split(IV_SEPARATOR.toRegex()).toTypedArray()
if (parts.size != 2) {
Log.e("TAG", "Invalid chipherText format")
return ""
}
// Decode IV and ciphertext from Base64
val iv = Base64.decode(parts[0], Base64.DEFAULT)
val encrypted = Base64.decode(parts[1], Base64.DEFAULT)
// Decrypt using the provided IV and ciphertext
val cipher = Cipher.getInstance(AES_MODE)
val ivParams = GCMParameterSpec(GCM_TAG_LENGTH, iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams)
val plaintextBytes = cipher.doFinal(encrypted)
String(plaintextBytes, StandardCharsets.UTF_8)
} catch (e: Exception) {
e.printStackTrace()
""
}
}
// encryption, decryption using RSA, Android Keystore
private const val Key_Name = "key12345"
private const val KEY_ALGORITHM_RSA = "RSA"
private const val TRANSFORMATION_ALGO = "RSA/ECB/PKCS1Padding"
private fun generateKeyPair(): Pair<PublicKey, PrivateKey> {
val keypairGen: KeyPairGenerator = KeyPairGenerator.getInstance(
KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val keyPairGeneratorSpec: KeyGenParameterSpec = KeyGenParameterSpec
.Builder(Key_Name, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setRandomizedEncryptionRequired(false)
.build()
keypairGen.initialize(keyPairGeneratorSpec, SecureRandom())
} else keypairGen.initialize(2048)
val keyPair = keypairGen.genKeyPair()
return Pair(keyPair.public, keyPair.private)
}
@RequiresApi(Build.VERSION_CODES.M)
fun getOrGenerateKey(): Pair<PublicKey, PrivateKey> {
val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
keyStore.load(null)
return if (keyStore.containsAlias(Key_Name)) {
val publicKey: PublicKey = keyStore.getCertificate(Key_Name).publicKey
val privateKeyEntry: KeyStore.PrivateKeyEntry =
keyStore.getEntry(Key_Name, null) as KeyStore.PrivateKeyEntry
val privateKey: PrivateKey = privateKeyEntry.privateKey
Pair(publicKey, privateKey)
} else generateKeyPair()
}
fun fromPlainToCipherText(
plaintext: String,
publicKey: PublicKey
): String {
return try {
val cipher = Cipher.getInstance(TRANSFORMATION_ALGO)
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val encryptedByte = cipher.doFinal(plaintext.toByteArray())
Base64.encodeToString(encryptedByte, Base64.DEFAULT)
} catch (e: Exception) {
e.printStackTrace()
""
}
}
fun fromCipherToPlainText(
cipherText: String,
privateKey: PrivateKey
): String {
return try {
val cipher1 = Cipher.getInstance(TRANSFORMATION_ALGO)
cipher1.init(Cipher.DECRYPT_MODE, privateKey)
val decryptedBytes = cipher1.doFinal(Base64.decode(cipherText, Base64.DEFAULT))
String(decryptedBytes)
} catch (e: Exception) {
e.printStackTrace()
""
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment