I'm trying to use javax.crypto.Cipher on Android to encrypt a stream of data in chunks using AES-GCM. As I understand, one can use Cipher.update multiple times for a multi-part encryption operation, and finalize with Cipher.doFinal. However when using the AES/GCM/NoPadding transformation, Cipher.update refuses to output data to the provided buffer, and returns 0 bytes written. The buffer builds up inside the Cipher until I call .doFinal. This also appears to happen with CCM (and I assume other authenticated modes), but works for other modes like CBC.
I figured GCM can compute the authentication tag while encrypting, so I'm not sure why I'm not allowed to consume the buffer in the Cipher.
I've made an example with just one call to .update: (kotlin)
val secretKey = KeyGenerator.getInstance("AES").run {
init(256)
generateKey()
}
val iv = ByteArray(12)
SecureRandom().nextBytes(iv)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
// Pretend this is some file I want to read and encrypt
val inputBuffer = Random.nextBytes(1024000)
val outputBuffer = ByteArray(cipher.getOutputSize(512))
val read = cipher.update(inputBuffer, 0, 512, outputBuffer, 0)
// ^ at this point, read = 0 and outputBuffer is [0, 0, 0, ...]
// Future calls to cipher.update and cipher.getOutputSize indicate that
// the internal buffer is growing. But I would like to consume it through
// outputBuffer
// ...
cipher.doFinal(outputBuffer, 0)
// Now outputBuffer is populated
What I would like to do is stream a large file from disk, encrypt it and send it over the network chunk by chunk, without having to load the entire file data into memory. I've tried to use CipherInputStream but it suffers from the same problem.
Is this possible with AES/GCM?