When custom IV value is generated, simply initialize Cipher instance with it:

cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))

cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))

Note, generated value must be passed into init() method for both encryption and decryption modes.

Empty Value

It is possible, but highly not recommended, to cheat on Cipher . Instead of generating new random Initialization Vector’s data each time before encryption, and then saving and parsing it before decryption, IV can be initialized once, as an array, with fixed length of static values and used everywhere, all the time:

// Create an array of 16 bytes, filled with 0 [0, 0, 0, 0 ..0]

val iv = ByteArray(16) // Use this array during encryption and decryption as IV data

cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))

cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))

Keep in mind that this implementation kills the essence of the IV.

Randomized Encryption

To protect users data from using incorrect (not random or empty) IV, by default, AndroidKeyStore Provider is not allowing to use custom IV values :

val iv = ByteArray(16)

cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv)) // will throw InvalidAlgorithmParameterException: Caller-provided IV // not permitted

cipher.doFinal(data.toByteArray())

On API 23, the setRandomizedEncryptionRequired method was added to KeyGenParameterSpec class, that should allow you to control whether IV may be custom or not. From documentation:

Sets whether encryption using this key must be sufficiently randomized to produce different ciphertexts for the same plaintext every time.

…

When IND-CPA is required: in block modes which use an IV, such as GCM, CBC, and CTR, caller-provided IVs are rejected when encrypting, to ensure that only random IVs are used.

val builder = KeyGenParameterSpec.Builder() // Forces to use only Default Generated, by Cipher, IV.

// Default value.

builder.setRandomizedEncryptionRequired(true) // Enables to use Custom Generated IV's.

// Not working.

builder.setRandomizedEncryptionRequired(false)

But it is not working, (at least for AES and CBC ). Even after disabling randomization, Cipher still crashes with InvalidAlgorithmParameterException and continue to require the Default Initialization Vector.

Usage Example

Lets encrypt and decrypt message using symmetric AES keys with Initialization Vector:

Full source code is available here.

Whats Next

In next “Key Invalidation” article from “Secure data in Android” series:

Just try to change Lock Screen type and all of your AndroidKeyStore keys will gone…

Security Tips