I can generate a valid CMS EnvelopedData value with AES-CBC using the Kotlin code below:
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cms.CMSAlgorithm
import org.bouncycastle.cms.CMSEnvelopedDataGenerator
import org.bouncycastle.cms.CMSProcessableByteArray
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator
fun encrypt(plaintext: ByteArray, recipientCertificate: X509CertificateHolder): ByteArray {
val cmsEnvelopedDataGenerator = CMSEnvelopedDataGenerator()
val x509Certificate = JcaX509CertificateConverter()
.getCertificate(recipientCertificate)
val transKeyGen =
JceKeyTransRecipientInfoGenerator(x509Certificate)
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(transKeyGen)
val msg = CMSProcessableByteArray(plaintext)
val encryptor = JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).build()
val bcEnvelopedData = cmsEnvelopedDataGenerator.generate(msg, encryptor)
return bcEnvelopedData.encoded
}
But if I replace CMSAlgorithm.AES128_CBC
with CMSAlgorithm.AES128_GCM
, JceCMSContentEncryptorBuilder.build()
throws the following error:
cannot create key generator: 2.16.840.1.101.3.4.1.6 KeyGenerator not available
That error seems to suggest AES-GCM-128 isn't supported, but the fact the object CMSAlgorithm.AES128_GCM
exists suggests to me that can't be it -- I must be doing something wrong. Maybe the IV isn't generated behind the scenes for me and I have to set it explicitly somehow?
I'm using org.bouncycastle:bcpkix-jdk15on:1.64
.
UPDATE: I've found a test in the BouncyCastle test suite that uses AES-GCM in EnvelopedData values, and they're also passing CMSAlgorithm.AES128_GCM
to JceCMSContentEncryptorBuilder
in the same way. The only difference is that they're calling .setProvider("BC")
before .build()
, but I've just tried that and it didn't make any difference.
Answering my question:
JceCMSContentEncryptorBuilder.setProvider()
must be certainly called before .build()
, but if the provider isn't registered, you have to pass an instance of org.bouncycastle.jce.provider.BouncyCastleProvider
.