From 1102d4556b586323ed014ca25751961a51e8c363 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 28 Aug 2024 14:48:52 +0100 Subject: [PATCH] Add auth enveloped recipients for KEK and KeyAgree Currently it is possible to use CMSAuthEnvelopedDataParser only with KeyTrans. This adds support for KEK and KeyAgree. The AEADInputDecryptor is separated to CMSInputAEADDecryptor as it is the same for all recipient types. A new test is added to properly test CMSAuthEnvelopedDataParser usage. --- .../cms/CMSInputAEADDecryptor.java | 78 +++ .../jcajce/JceKEKAuthEnvelopedRecipient.java | 29 + .../JceKeyAgreeAuthEnvelopedRecipient.java | 31 + .../JceKeyTransAuthEnvelopedRecipient.java | 63 +- .../org/bouncycastle/cms/test/AllTests.java | 1 + .../test/NewAuthEnvelopedDataStreamTest.java | 557 ++++++++++++++++++ .../cms/test/NewEnvelopedDataStreamTest.java | 2 - 7 files changed, 698 insertions(+), 63 deletions(-) create mode 100644 pkix/src/main/java/org/bouncycastle/cms/CMSInputAEADDecryptor.java create mode 100644 pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKAuthEnvelopedRecipient.java create mode 100644 pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeAuthEnvelopedRecipient.java create mode 100644 pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSInputAEADDecryptor.java b/pkix/src/main/java/org/bouncycastle/cms/CMSInputAEADDecryptor.java new file mode 100644 index 0000000000..c216b36f5a --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSInputAEADDecryptor.java @@ -0,0 +1,78 @@ +package org.bouncycastle.cms; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.jcajce.JceKEKAuthEnvelopedRecipient; +import org.bouncycastle.jcajce.io.CipherInputStream; +import org.bouncycastle.operator.InputAEADDecryptor; + +import javax.crypto.Cipher; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class CMSInputAEADDecryptor + implements InputAEADDecryptor +{ + final AlgorithmIdentifier contentEncryptionAlgorithm; + + final Cipher dataCipher; + + private InputStream inputStream; + + public CMSInputAEADDecryptor(AlgorithmIdentifier contentEncryptionAlgorithm, Cipher dataCipher) + { + this.contentEncryptionAlgorithm = contentEncryptionAlgorithm; + this.dataCipher = dataCipher; + } + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return contentEncryptionAlgorithm; + } + + public InputStream getInputStream(InputStream dataIn) + { + inputStream = dataIn; + return new CipherInputStream(dataIn, dataCipher); + } + + public OutputStream getAADStream() + { + return new AADStream(dataCipher); + } + + public byte[] getMAC() + { + if (inputStream instanceof InputStreamWithMAC) + { + return ((InputStreamWithMAC)inputStream).getMAC(); + } + return null; + } + + private static class AADStream + extends OutputStream + { + private Cipher cipher; + private byte[] oneByte = new byte[1]; + + public AADStream(Cipher cipher) + { + this.cipher = cipher; + } + + public void write(byte[] buf, int off, int len) + throws IOException + { + cipher.updateAAD(buf, off, len); + } + + public void write(int b) + throws IOException + { + oneByte[0] = (byte)b; + + cipher.updateAAD(oneByte); + } + } +} diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKAuthEnvelopedRecipient.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKAuthEnvelopedRecipient.java new file mode 100644 index 0000000000..e26b1097a6 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKAuthEnvelopedRecipient.java @@ -0,0 +1,29 @@ +package org.bouncycastle.cms.jcajce; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSInputAEADDecryptor; +import org.bouncycastle.cms.RecipientOperator; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import java.security.Key; + +public class JceKEKAuthEnvelopedRecipient + extends JceKEKRecipient +{ + public JceKEKAuthEnvelopedRecipient(SecretKey recipientKey) + { + super(recipientKey); + } + + public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey) + throws CMSException + { + Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentEncryptionAlgorithm, encryptedContentEncryptionKey); + + final Cipher dataCipher = contentHelper.createContentCipher(secretKey, contentEncryptionAlgorithm); + + return new RecipientOperator(new CMSInputAEADDecryptor(contentEncryptionAlgorithm, dataCipher)); + } +} diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeAuthEnvelopedRecipient.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeAuthEnvelopedRecipient.java new file mode 100644 index 0000000000..7d80cc9308 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeAuthEnvelopedRecipient.java @@ -0,0 +1,31 @@ +package org.bouncycastle.cms.jcajce; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSInputAEADDecryptor; +import org.bouncycastle.cms.RecipientOperator; + +import javax.crypto.Cipher; +import java.security.Key; +import java.security.PrivateKey; + +public class JceKeyAgreeAuthEnvelopedRecipient + extends JceKeyAgreeRecipient +{ + public JceKeyAgreeAuthEnvelopedRecipient(PrivateKey recipientKey) + { + super(recipientKey); + } + + public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentEncryptionAlgorithm, SubjectPublicKeyInfo senderPublicKey, ASN1OctetString userKeyingMaterial, byte[] encryptedContentKey) + throws CMSException + { + Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentEncryptionAlgorithm, senderPublicKey, userKeyingMaterial, encryptedContentKey); + + final Cipher dataCipher = contentHelper.createContentCipher(secretKey, contentEncryptionAlgorithm); + + return new RecipientOperator(new CMSInputAEADDecryptor(contentEncryptionAlgorithm, dataCipher)); + } +} diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransAuthEnvelopedRecipient.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransAuthEnvelopedRecipient.java index c697ca83f5..df600757a5 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransAuthEnvelopedRecipient.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransAuthEnvelopedRecipient.java @@ -1,8 +1,5 @@ package org.bouncycastle.cms.jcajce; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.security.Key; import java.security.PrivateKey; @@ -10,10 +7,8 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cms.CMSException; -import org.bouncycastle.cms.InputStreamWithMAC; +import org.bouncycastle.cms.CMSInputAEADDecryptor; import org.bouncycastle.cms.RecipientOperator; -import org.bouncycastle.jcajce.io.CipherInputStream; -import org.bouncycastle.operator.InputAEADDecryptor; public class JceKeyTransAuthEnvelopedRecipient extends JceKeyTransRecipient @@ -30,60 +25,6 @@ public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionA final Cipher dataCipher = contentHelper.createContentCipher(secretKey, contentEncryptionAlgorithm); - return new RecipientOperator(new InputAEADDecryptor() - { - private InputStream inputStream; - - public AlgorithmIdentifier getAlgorithmIdentifier() - { - return contentEncryptionAlgorithm; - } - - public InputStream getInputStream(InputStream dataIn) - { - inputStream = dataIn; - return new CipherInputStream(dataIn, dataCipher); - } - - public OutputStream getAADStream() - { - return new AADStream(dataCipher); - } - - public byte[] getMAC() - { - if (inputStream instanceof InputStreamWithMAC) - { - return ((InputStreamWithMAC)inputStream).getMAC(); - } - return null; - } - }); - } - - private static class AADStream - extends OutputStream - { - private Cipher cipher; - private byte[] oneByte = new byte[1]; - - public AADStream(Cipher cipher) - { - this.cipher = cipher; - } - - public void write(byte[] buf, int off, int len) - throws IOException - { - cipher.updateAAD(buf, off, len); - } - - public void write(int b) - throws IOException - { - oneByte[0] = (byte)b; - - cipher.updateAAD(oneByte); - } + return new RecipientOperator(new CMSInputAEADDecryptor(contentEncryptionAlgorithm, dataCipher)); } } diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/AllTests.java b/pkix/src/test/java/org/bouncycastle/cms/test/AllTests.java index edc7aff46d..fd911806be 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/AllTests.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/AllTests.java @@ -28,6 +28,7 @@ public static Test suite() suite.addTest(NewAuthenticatedDataStreamTest.suite()); suite.addTest(NewCompressedDataStreamTest.suite()); suite.addTest(NewSignedDataStreamTest.suite()); + suite.addTest(NewAuthEnvelopedDataStreamTest.suite()); suite.addTest(NewEnvelopedDataStreamTest.suite()); suite.addTest(AuthEnvelopedDataTest.suite()); diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java new file mode 100644 index 0000000000..3216813adb --- /dev/null +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java @@ -0,0 +1,557 @@ +package org.bouncycastle.cms.test; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.asn1.*; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.*; +import org.bouncycastle.cms.jcajce.*; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.OutputAEADEncryptor; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; + +import javax.crypto.SecretKey; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; + +public class NewAuthEnvelopedDataStreamTest + extends TestCase +{ + + private static final String BC = BouncyCastleProvider.PROVIDER_NAME; + + private static final int BUFFER_SIZE = 4000; + private static String _signDN; + private static KeyPair _signKP; + private static X509Certificate _signCert; + + private static String _origDN; + private static KeyPair _origKP; + private static X509Certificate _origCert; + + private static String _reciDN; + private static KeyPair _reciKP; + private static X509Certificate _reciCert; + + private static KeyPair _origEcKP; + private static KeyPair _reciEcKP; + private static X509Certificate _reciEcCert; + + private static boolean _initialised = false; + + public NewAuthEnvelopedDataStreamTest() + { + } + + private static void init() + throws Exception + { + if (!_initialised) + { + _initialised = true; + + _signDN = "O=Bouncy Castle, C=AU"; + _signKP = CMSTestUtil.makeKeyPair(); + _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN); + + _origDN = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU"; + _origKP = CMSTestUtil.makeKeyPair(); + _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN); + + _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU"; + _reciKP = CMSTestUtil.makeKeyPair(); + _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN); + + _origEcKP = CMSTestUtil.makeEcDsaKeyPair(); + _reciEcKP = CMSTestUtil.makeEcDsaKeyPair(); + _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN); + } + } + + public void setUp() + throws Exception + { + init(); + } + + private void verifyData( + ByteArrayOutputStream encodedStream, + String expectedOid, + byte[] expectedData) + throws Exception + { + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(encodedStream.toByteArray()); + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), expectedOid); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = (RecipientInformation)it.next(); + + assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); + + CMSTypedStream recData = recipient.getContentStream(new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + assertTrue(Arrays.equals(expectedData, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + } + } + + public void testUnprotectedAttributes() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + + CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator(); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + Hashtable attrs = new Hashtable<>(); + + attrs.put(PKCSObjectIdentifiers.id_aa_contentHint, new Attribute(PKCSObjectIdentifiers.id_aa_contentHint, new DERSet(new DERUTF8String("Hint")))); + attrs.put(PKCSObjectIdentifiers.id_aa_receiptRequest, new Attribute(PKCSObjectIdentifiers.id_aa_receiptRequest, new DERSet(new DERUTF8String("Request")))); + + AttributeTable attrTable = new AttributeTable(attrs); + + edGen.setUnprotectedAttributeGenerator(new SimpleAttributeTableGenerator(attrTable)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream out = edGen.open( + bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM).setProvider(BC).build()); + + out.write(data); + + out.close(); + + CMSEnvelopedDataParser ed = new CMSEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ed.getRecipientInfos(); + + Collection c = recipients.getRecipients(); + + assertEquals(1, c.size()); + + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); + + byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + assertTrue(Arrays.equals(data, recData)); + } + + attrTable = ed.getUnprotectedAttributes(); + + assertEquals(attrs.size(), 2); + + assertEquals(new DERUTF8String("Hint"), attrTable.get(PKCSObjectIdentifiers.id_aa_contentHint).getAttrValues().getObjectAt(0)); + assertEquals(new DERUTF8String("Request"), attrTable.get(PKCSObjectIdentifiers.id_aa_receiptRequest).getAttrValues().getObjectAt(0)); + } + + public void testKeyTransAES128GCM() + throws Exception + { + byte[] data = new byte[2000]; + + for (int i = 0; i != 2000; i++) + { + data[i] = (byte)(i & 0xff); + } + + // + // unbuffered + // + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + for (int i = 0; i != 2000; i++) + { + out.write(data[i]); + } + + out.close(); + + verifyData(bOut, CMSAlgorithm.AES128_GCM.getId(), data); + + int unbufferedLength = bOut.toByteArray().length; + + // + // Using buffered output - should be == to unbuffered + // + edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + bOut = new ByteArrayOutputStream(); + + out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + BufferedOutputStream bfOut = new BufferedOutputStream(out, 300); + + for (int i = 0; i != 2000; i++) + { + bfOut.write(data[i]); + } + + bfOut.close(); + + verifyData(bOut, CMSAlgorithm.AES128_GCM.getId(), data); + + assertEquals(bOut.toByteArray().length, unbufferedLength); + } + + public void testKeyTransAES128Der() + throws Exception + { + byte[] data = new byte[2000]; + + for (int i = 0; i != 2000; i++) + { + data[i] = (byte)(i & 0xff); + } + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + for (int i = 0; i != 2000; i++) + { + out.write(data[i]); + } + + out.close(); + + // convert to DER + ASN1InputStream aIn = new ASN1InputStream(bOut.toByteArray()); + + bOut.reset(); + + aIn.readObject().encodeTo(bOut, ASN1Encoding.DER); + + verifyData(bOut, CMSAlgorithm.AES128_GCM.getId(), data); + } + + public void testKeyTransAES128Throughput() + throws Exception + { + byte[] data = new byte[40001]; + + for (int i = 0; i != data.length; i++) + { + data[i] = (byte)(i & 0xff); + } + + // + // buffered + // + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + edGen.setBufferSize(BUFFER_SIZE); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + for (int i = 0; i != data.length; i++) + { + out.write(data[i]); + } + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + RecipientInformationStore recipients = ep.getRecipientInfos(); + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + if (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); + + CMSTypedStream recData = recipient.getContentStream( + new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + InputStream dataStream = recData.getContentStream(); + ByteArrayOutputStream dataOut = new ByteArrayOutputStream(); + int len; + byte[] buf = new byte[BUFFER_SIZE]; + int count = 0; + + while (count != 10 && (len = dataStream.read(buf)) > 0) + { + assertEquals(buf.length, len); + + dataOut.write(buf); + count++; + } + + len = dataStream.read(buf); + dataOut.write(buf, 0, len); + + assertEquals(true, Arrays.equals(data, dataOut.toByteArray())); + } + else + { + fail("recipient not found."); + } + } + + public void testKeyTransAES128AndOriginatorInfo() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + X509CertificateHolder origCert = new X509CertificateHolder(_origCert.getEncoded()); + + edGen.setOriginatorInfo(new OriginatorInfoGenerator(origCert).generate()); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + assertTrue(ep.getOriginatorInfo().getCertificates().getMatches(null).contains(origCert)); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.AES128_GCM.getId()); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); + + CMSTypedStream recData = recipient.getContentStream( + new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + } + + ep.close(); + } + + public void testKeyTransAES128() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.AES128_GCM.getId()); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); + + CMSTypedStream recData = recipient.getContentStream(new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + } + + ep.close(); + } + + public void testAESKEK() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + SecretKey kek = CMSTestUtil.makeAES192Key(); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + byte[] kekId = new byte[]{1, 2, 3, 4, 5}; + + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId, kek).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.AES128_GCM.getId()); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + CMSTypedStream recData = recipient.getContentStream(new JceKEKAuthEnvelopedRecipient(kek).setProvider(BC)); + + assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + } + + ep.close(); + } + + public void testTwoAESKEK() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + SecretKey kek1 = CMSTestUtil.makeAES192Key(); + SecretKey kek2 = CMSTestUtil.makeAES192Key(); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + byte[] kekId1 = new byte[]{1, 2, 3, 4, 5}; + byte[] kekId2 = new byte[]{5, 4, 3, 2, 1}; + + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId1, kek1).setProvider(BC)); + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId2, kek2).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES192_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.AES192_GCM.getId()); + + RecipientId recSel = new KEKRecipientId(kekId2); + + RecipientInformation recipient = recipients.get(recSel); + + CMSTypedStream recData = recipient.getContentStream(new JceKEKAuthEnvelopedRecipient(kek2).setProvider(BC)); + + assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + + ep.close(); + } + + public void testECKeyAgree() + throws Exception + { + byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65"); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator( + CMSAlgorithm.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), + CMSAlgorithm.AES128_WRAP).setProvider(BC); + + recipientGenerator.addRecipient(_reciEcCert); + + edGen.addRecipientInfoGenerator(recipientGenerator); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.AES128_GCM.getId()); + + RecipientId recSel = new JceKeyAgreeRecipientId(_reciEcCert); + + RecipientInformation recipient = recipients.get(recSel); + + CMSTypedStream recData = recipient.getContentStream( + new JceKeyAgreeAuthEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC)); + + assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + + ep.close(); + } + + public static Test suite() + throws Exception + { + return new CMSTestSetup(new TestSuite(NewAuthEnvelopedDataStreamTest.class)); + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java index 535bbcc556..4786f62f30 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java @@ -30,7 +30,6 @@ import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSAlgorithm; -import org.bouncycastle.cms.CMSAuthEnvelopedDataStreamGenerator; import org.bouncycastle.cms.CMSEnvelopedDataGenerator; import org.bouncycastle.cms.CMSEnvelopedDataParser; import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator; @@ -51,7 +50,6 @@ import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.OutputAEADEncryptor; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex;