Cryptography in Kotlin: Encryption and Hashing
Kotlin, running on top of the JVM, provides full access to powerful Java cryptographic libraries such as Java Cryptography Extension (JCE) and Bouncy Castle. In this article, we will look at how to implement symmetric (AES), asymmetric (RSA) encryption, as well as hashing (SHA-256) in Kotlin. All code examples are ready to run — you can copy them into your project.
Project Setup
To begin, add the Bouncy Castle dependency (a recommended provider for modern algorithms) to your build.gradle.kts:
dependencies { implementation("org.bouncycastle:bcprov-jdk15on:1.70")}After syncing Gradle, import the provider in your code:
import org.bouncycastle.jce.provider.BouncyCastleProviderimport java.security.Security
fun initCrypto() { Security.addProvider(BouncyCastleProvider())}1. SHA-256 Hashing
Hashing is an irreversible transformation of data. It is most often used for verifying file integrity and storing passwords.
import java.security.MessageDigest
fun sha256(input: String): String { val digest = MessageDigest.getInstance("SHA-256") val hashBytes = digest.digest(input.toByteArray()) return hashBytes.joinToString("") { "%02x".format(it) }}
fun main() { val password = "mySecret123" val hash = sha256(password) println("SHA-256 hash: $hash") // Output: SHA-256 hash: a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3}Important: For passwords, use bcrypt or scrypt — SHA-256 is too fast and vulnerable to brute-force attacks.
2. Symmetric AES Encryption
AES (Advanced Encryption Standard) is the most popular algorithm for data encryption. We use GCM mode (which guarantees authenticity) and a random IV.
import javax.crypto.Cipherimport javax.crypto.KeyGeneratorimport javax.crypto.SecretKeyimport javax.crypto.spec.GCMParameterSpecimport java.util.Base64
class AesGcmEncryption { companion object { private const val AES_KEY_SIZE = 256 private const val GCM_IV_LENGTH = 12 private const val GCM_TAG_LENGTH = 128 }
fun generateKey(): SecretKey { val keyGen = KeyGenerator.getInstance("AES") keyGen.init(AES_KEY_SIZE) return keyGen.generateKey() }
fun encrypt(plainText: String, secretKey: SecretKey): String { val cipher = Cipher.getInstance("AES/GCM/NoPadding") val iv = ByteArray(GCM_IV_LENGTH).apply { java.security.SecureRandom().nextBytes(this) } cipher.init(Cipher.ENCRYPT_MODE, secretKey, GCMParameterSpec(GCM_TAG_LENGTH, iv)) val cipherText = cipher.doFinal(plainText.toByteArray()) // Combine IV and ciphertext val combined = iv + cipherText return Base64.getEncoder().encodeToString(combined) }
fun decrypt(encryptedData: String, secretKey: SecretKey): String { val combined = Base64.getDecoder().decode(encryptedData) val iv = combined.copyOfRange(0, GCM_IV_LENGTH) val cipherText = combined.copyOfRange(GCM_IV_LENGTH, combined.size) val cipher = Cipher.getInstance("AES/GCM/NoPadding") cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(GCM_TAG_LENGTH, iv)) return String(cipher.doFinal(cipherText)) }}
fun main() { val aes = AesGcmEncryption() val key = aes.generateKey() val original = "Secret message" val encrypted = aes.encrypt(original, key) val decrypted = aes.decrypt(encrypted, key) println("Original: $original") println("Encrypted: $encrypted") println("Decrypted: $decrypted")}3. Asymmetric
Also in library
Monitoring Swift Applications on iOS/macOS: Libraries and Tools
Rust Application Configuration: The `config` Library for Flexible Settings