Skip to content

Symmetric Encryption

Symmetric encryption uses a single shared key to encrypt and decrypt data. The modes on this page – AES-CBC, AES-CTR, and AES-ECB – provide confidentiality only. They do not detect tampering or verify integrity of the ciphertext.

Prefer AEAD for new applications

For most use cases, AEAD algorithms like AES-GCM provide both encryption and authentication. Use the modes on this page only when a protocol or legacy system requires them.

Assumed imports

import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.algorithms.*

val provider = CryptographyProvider.Default

Basic Usage

Get the algorithm, generate a key, create a cipher, and encrypt/decrypt. AES-CBC enables PKCS#7 padding by default, so plaintext of any length is accepted:

val aesCbc = provider.get(AES.CBC)
val key = aesCbc.keyGenerator().generateKey()
val cipher = key.cipher() // padding enabled by default

// Encrypt
val ciphertext = cipher.encrypt(plaintext = "secret message".encodeToByteArray())

// Decrypt
val plaintext = cipher.decrypt(ciphertext = ciphertext)
println(plaintext.decodeToString()) // secret message

Each call to encrypt generates a fresh random IV and prepends it to the output: [IV | ciphertext]. When you pass this to decrypt, the library splits it automatically. The cipher is reusable – call encrypt and decrypt as many times as needed.

To disable padding (plaintext must then be a multiple of 16 bytes):

val cipher = key.cipher(padding = false)

Custom IV

By default, a random IV is generated and prepended to the ciphertext. If your protocol requires a specific IV, use encryptWithIv and decryptWithIv:

val cipher = key.cipher()
val iv = ByteArray(16) // 16 bytes for AES-CBC

val ciphertext = cipher.encryptWithIv(
    iv = iv,
    plaintext = "secret".encodeToByteArray()
)

// With custom IV, the output does NOT contain the IV -- only the ciphertext
val plaintext = cipher.decryptWithIv(
    iv = iv,
    ciphertext = ciphertext
)

Warning

Reusing an IV with the same key weakens or breaks encryption security depending on the mode. Only use custom IVs when you have a reliable mechanism to guarantee uniqueness.

Streaming

For large data that does not fit in memory, use the kotlinx-io streaming API. The cipher provides an ability to transform RawSource via encryptingSource and decryptingSource as well as RawSink with encryptingSink and decryptingSink:

val cipher = key.cipher()

// Pull-based: wrap a source
val encryptedSource: RawSource = cipher.encryptingSource(plaintextSource)
val decryptedSource: RawSource = cipher.decryptingSource(ciphertextSource)

// Push-based: wrap a sink
val encryptingSink: RawSink = cipher.encryptingSink(destinationSink)
val decryptingSink: RawSink = cipher.decryptingSink(plaintextSink)

Custom IV variants are also available: encryptingSourceWithIv, encryptingSinkWithIv, decryptingSourceWithIv, decryptingSinkWithIv.

Supported Algorithms

Algorithm JDK WebCrypto Apple CryptoKit OpenSSL3
AES-CBC ✅ ✅ 1 2 ✅ ❌ ✅
AES-CTR ✅ ✅ 2 ✅ ❌ ✅
AES-ECB ☣ ✅ ❌ ✅ ❌ ✅
AES-OFB ⚠ ✅ ❌ ✅ ❌ ✅
AES-CFB ⚠ ✅ ❌ ✅ ❌ ✅
AES-CFB8 ⚠ ✅ ❌ ✅ ❌ ✅

  1. Only padding=true supported 

  2. 192-bit keys may not be supported in some browsers