Created
July 14, 2024 19:07
-
-
Save shishirthedev/61e342b1a39c7ce7b988b25f8511ddd9 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| 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