diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnveloped.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnveloped.java new file mode 100644 index 0000000000..7b6b9b8bc9 --- /dev/null +++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnveloped.java @@ -0,0 +1,41 @@ +package org.bouncycastle.mail.smime; + +import org.bouncycastle.cms.CMSAuthEnvelopedData; +import org.bouncycastle.cms.CMSException; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimePart; + +/** + * containing class for an S/MIME pkcs7-mime encrypted MimePart. + */ +public class SMIMEAuthEnveloped + extends CMSAuthEnvelopedData +{ + MimePart message; + + public SMIMEAuthEnveloped( + MimeBodyPart message) + throws MessagingException, CMSException + { + super(SMIMEUtil.getInputStream(message)); + + this.message = message; + } + + public SMIMEAuthEnveloped( + MimeMessage message) + throws MessagingException, CMSException + { + super(SMIMEUtil.getInputStream(message)); + + this.message = message; + } + + public MimePart getEncryptedContent() + { + return message; + } +} diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java new file mode 100644 index 0000000000..2a23826729 --- /dev/null +++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java @@ -0,0 +1,191 @@ +package org.bouncycastle.mail.smime; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.cms.*; +import org.bouncycastle.operator.OutputAEADEncryptor; +import org.bouncycastle.operator.OutputEncryptor; + +import javax.activation.CommandMap; +import javax.activation.MailcapCommandMap; +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import java.io.IOException; +import java.io.OutputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * General class for generating a pkcs7-mime message using AEAD algorithm. + * + * A simple example of usage. + * + *
+ * SMIMEAuthEnvelopedGenerator fact = new SMIMEAuthEnvelopedGenerator();
+ *
+ * fact.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
+ *
+ * MimeBodyPart mp = fact.generate(content, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider("BC").build());
+ *
+ *
+ * Note: Most clients expect the MimeBodyPart to be in a MimeMultipart
+ * when it's sent.
+ */
+public class SMIMEAuthEnvelopedGenerator
+ extends SMIMEEnvelopedGenerator
+{
+ public static final String AES128_GCM = CMSAuthEnvelopedDataGenerator.AES128_GCM;
+ public static final String AES192_GCM = CMSAuthEnvelopedDataGenerator.AES192_GCM;
+ public static final String AES256_GCM = CMSAuthEnvelopedDataGenerator.AES256_GCM;
+
+ private static final String AUTH_ENCRYPTED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=authEnveloped-data";
+
+ final private AuthEnvelopedGenerator authFact;
+
+ static
+ {
+ AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ CommandMap commandMap = CommandMap.getDefaultCommandMap();
+
+ if (commandMap instanceof MailcapCommandMap)
+ {
+ CommandMap.setDefaultCommandMap(MailcapUtil.addCommands((MailcapCommandMap)commandMap));
+ }
+
+ return null;
+ }
+ });
+ }
+
+ /**
+ * base constructor
+ */
+ public SMIMEAuthEnvelopedGenerator()
+ {
+ authFact = new AuthEnvelopedGenerator();
+ }
+
+ /**
+ * add a recipientInfoGenerator.
+ */
+ public void addRecipientInfoGenerator(
+ RecipientInfoGenerator recipientInfoGen)
+ throws IllegalArgumentException
+ {
+ authFact.addRecipientInfoGenerator(recipientInfoGen);
+ }
+
+ /**
+ * Use a BER Set to store the recipient information
+ */
+ public void setBerEncodeRecipients(
+ boolean berEncodeRecipientSet)
+ {
+ authFact.setBEREncodeRecipients(berEncodeRecipientSet);
+ }
+
+ /**
+ * return encrypted content type for enveloped data.
+ */
+ protected String getEncryptedContentType() {
+ return AUTH_ENCRYPTED_CONTENT_TYPE;
+ }
+
+ /**
+ * return content encryptor.
+ */
+ protected SMIMEStreamingProcessor getContentEncryptor(
+ MimeBodyPart content,
+ OutputEncryptor encryptor)
+ throws SMIMEException
+ {
+ if (encryptor instanceof OutputAEADEncryptor) {
+ return new ContentEncryptor(content, (OutputAEADEncryptor)encryptor);
+ }
+ // this would happen if the encryption algorithm is not AEAD algorithm
+ throw new SMIMEException("encryptor is not AEAD encryptor");
+ }
+
+ private static class AuthEnvelopedGenerator
+ extends CMSAuthEnvelopedDataStreamGenerator
+ {
+ private ASN1ObjectIdentifier dataType;
+ private ASN1EncodableVector recipientInfos;
+
+ protected OutputStream open(
+ ASN1ObjectIdentifier dataType,
+ OutputStream out,
+ ASN1EncodableVector recipientInfos,
+ OutputAEADEncryptor encryptor)
+ throws IOException
+ {
+ this.dataType = dataType;
+ this.recipientInfos = recipientInfos;
+
+ return super.open(dataType, out, recipientInfos, encryptor);
+ }
+
+ OutputStream regenerate(
+ OutputStream out,
+ OutputAEADEncryptor encryptor)
+ throws IOException
+ {
+ return super.open(dataType, out, recipientInfos, encryptor);
+ }
+ }
+
+ private class ContentEncryptor
+ implements SMIMEStreamingProcessor
+ {
+ private final MimeBodyPart _content;
+ private OutputAEADEncryptor _encryptor;
+
+ private boolean _firstTime = true;
+
+ ContentEncryptor(
+ MimeBodyPart content,
+ OutputAEADEncryptor encryptor)
+ {
+ _content = content;
+ _encryptor = encryptor;
+ }
+
+ public void write(OutputStream out)
+ throws IOException
+ {
+ OutputStream encrypted;
+
+ try
+ {
+ if (_firstTime)
+ {
+ encrypted = authFact.open(out, _encryptor);
+
+ _firstTime = false;
+ }
+ else
+ {
+ encrypted = authFact.regenerate(out, _encryptor);
+ }
+
+ CommandMap commandMap = CommandMap.getDefaultCommandMap();
+
+ if (commandMap instanceof MailcapCommandMap)
+ {
+ _content.getDataHandler().setCommandMap(MailcapUtil.addCommands((MailcapCommandMap)commandMap));
+ }
+
+ _content.writeTo(encrypted);
+
+ encrypted.close();
+ }
+ catch (MessagingException | CMSException e)
+ {
+ throw new WrappingIOException(e.toString(), e);
+ }
+ }
+ }
+}
diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedParser.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedParser.java
new file mode 100644
index 0000000000..e44f69c2d0
--- /dev/null
+++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedParser.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.mail.smime;
+
+import org.bouncycastle.cms.CMSAuthEnvelopedDataParser;
+import org.bouncycastle.cms.CMSException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimePart;
+import java.io.IOException;
+
+/**
+ * Stream based containing class for an S/MIME pkcs7-mime encrypted MimePart using AEAD algorithm.
+ */
+public class SMIMEAuthEnvelopedParser
+ extends CMSAuthEnvelopedDataParser
+{
+ private final MimePart message;
+
+ public SMIMEAuthEnvelopedParser(
+ MimeBodyPart message)
+ throws IOException, MessagingException, CMSException
+ {
+ this(message, 0);
+ }
+
+ public SMIMEAuthEnvelopedParser(
+ MimeMessage message)
+ throws IOException, MessagingException, CMSException
+ {
+ this(message, 0);
+ }
+
+ /**
+ * Create a parser from a MimeBodyPart using the passed in buffer size
+ * for reading it.
+ *
+ * @param message body part to be parsed.
+ * @param bufferSize bufferSoze to be used.
+ */
+ public SMIMEAuthEnvelopedParser(
+ MimeBodyPart message,
+ int bufferSize)
+ throws IOException, MessagingException, CMSException
+ {
+ super(SMIMEUtil.getInputStream(message, bufferSize));
+
+ this.message = message;
+ }
+
+ /**
+ * Create a parser from a MimeMessage using the passed in buffer size
+ * for reading it.
+ *
+ * @param message message to be parsed.
+ * @param bufferSize bufferSize to be used.
+ */
+ public SMIMEAuthEnvelopedParser(
+ MimeMessage message,
+ int bufferSize)
+ throws IOException, MessagingException, CMSException
+ {
+ super(SMIMEUtil.getInputStream(message, bufferSize));
+
+ this.message = message;
+ }
+
+ public MimePart getEncryptedContent()
+ {
+ return message;
+ }
+}
diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEEnvelopedGenerator.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEEnvelopedGenerator.java
index e431726567..a30864c3d2 100644
--- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEEnvelopedGenerator.java
+++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEEnvelopedGenerator.java
@@ -112,7 +112,25 @@ public void setBerEncodeRecipients(
fact.setBEREncodeRecipients(berEncodeRecipientSet);
}
- /**
+ /**
+ * return encrypted content type for enveloped data.
+ */
+ protected String getEncryptedContentType() {
+ return ENCRYPTED_CONTENT_TYPE;
+ }
+
+ /**
+ * return content encryptor.
+ */
+ protected SMIMEStreamingProcessor getContentEncryptor(
+ MimeBodyPart content,
+ OutputEncryptor encryptor)
+ throws SMIMEException
+ {
+ return new ContentEncryptor(content, encryptor);
+ }
+
+ /**
* if we get here we expect the Mime body part to be well defined.
*/
private MimeBodyPart make(
@@ -124,8 +142,8 @@ private MimeBodyPart make(
{
MimeBodyPart data = new MimeBodyPart();
- data.setContent(new ContentEncryptor(content, encryptor), ENCRYPTED_CONTENT_TYPE);
- data.addHeader("Content-Type", ENCRYPTED_CONTENT_TYPE);
+ data.setContent(getContentEncryptor(content, encryptor), getEncryptedContentType());
+ data.addHeader("Content-Type", getEncryptedContentType());
data.addHeader("Content-Disposition", "attachment; filename=\"smime.p7m\"");
data.addHeader("Content-Description", "S/MIME Encrypted Message");
data.addHeader("Content-Transfer-Encoding", encoding);
@@ -210,11 +228,7 @@ public void write(OutputStream out)
encrypted.close();
}
- catch (MessagingException e)
- {
- throw new WrappingIOException(e.toString(), e);
- }
- catch (CMSException e)
+ catch (MessagingException | CMSException e)
{
throw new WrappingIOException(e.toString(), e);
}
@@ -249,7 +263,7 @@ OutputStream regenerate(
}
}
- private static class WrappingIOException
+ protected static class WrappingIOException
extends IOException
{
private Throwable cause;
diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java
index 65305772f1..662fc75958 100644
--- a/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java
+++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java
@@ -49,6 +49,7 @@ public static Test suite()
suite.addTestSuite(NewSMIMESignedTest.class);
suite.addTestSuite(SignedMailValidatorTest.class);
+ suite.addTestSuite(NewSMIMEAuthEnvelopedTest.class);
suite.addTestSuite(NewSMIMEEnvelopedTest.class);
suite.addTestSuite(SMIMECompressedTest.class);
suite.addTestSuite(SMIMEMiscTest.class);
diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java
new file mode 100644
index 0000000000..b7708797c2
--- /dev/null
+++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java
@@ -0,0 +1,501 @@
+package org.bouncycastle.mail.smime.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cms.*;
+import org.bouncycastle.cms.jcajce.*;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mail.smime.*;
+import org.bouncycastle.mail.smime.util.FileBackedMimeBodyPart;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.util.encoders.Base64;
+
+import javax.crypto.Cipher;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.*;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+public class NewSMIMEAuthEnvelopedTest
+ extends TestCase
+{
+ private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+ private static String _signDN;
+ private static KeyPair _signKP;
+
+ private static String _reciDN;
+ private static KeyPair _reciKP;
+ private static X509Certificate _reciCert;
+
+ private static String _reciDN2;
+ private static KeyPair _reciKP2;
+ private static X509Certificate _reciCert2;
+
+ private static KeyPair _origEcKP;
+ private static KeyPair _reciEcKP;
+ private static X509Certificate _reciEcCert;
+ private static KeyPair _reciEcKP2;
+ private static X509Certificate _reciEcCert2;
+
+ private static boolean _initialised = false;
+
+ static final byte[] testMessage = Base64.decode(
+ "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+ "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+ "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+ "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+ "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+ "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+ "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+ "wMTMyLS0NCg==");
+
+ private static void init()
+ throws Exception
+ {
+ if (!_initialised)
+ {
+ if (Security.getProvider("BC") == null)
+ {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ _initialised = true;
+
+ _signDN = "O=Bouncy Castle, C=AU";
+ _signKP = CMSTestUtil.makeKeyPair();
+
+ _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+ _reciKP = CMSTestUtil.makeKeyPair();
+ _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+
+ _reciDN2 = "CN=Fred, OU=Sales, O=Bouncy Castle, C=AU";
+ _reciKP2 = CMSTestUtil.makeKeyPair();
+ _reciCert2 = CMSTestUtil.makeCertificate(_reciKP2, _reciDN2, _signKP, _signDN);
+
+ _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
+ _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
+ _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN);
+ _reciEcKP2 = CMSTestUtil.makeEcDsaKeyPair();
+ _reciEcCert2 = CMSTestUtil.makeCertificate(_reciEcKP2, _reciDN2, _signKP, _signDN);
+ }
+ }
+
+ public NewSMIMEAuthEnvelopedTest(String name)
+ {
+ super(name);
+ }
+
+ public static void main(String[] args)
+ {
+ junit.textui.TestRunner.run(NewSMIMEAuthEnvelopedTest.class);
+ }
+
+ public static Test suite()
+ throws Exception
+ {
+ return new SMIMETestSetup(new TestSuite(NewSMIMEAuthEnvelopedTest.class));
+ }
+
+ public void setUp()
+ throws Exception
+ {
+ init();
+ }
+
+ private MimeMessage loadMessage(String name)
+ throws MessagingException, FileNotFoundException
+ {
+ Session session = Session.getDefaultInstance(System.getProperties(), null);
+
+ return new MimeMessage(session, getClass().getResourceAsStream(name));
+ }
+
+ private X509Certificate loadCert(String name)
+ throws Exception
+ {
+ return (X509Certificate)CertificateFactory.getInstance("X.509", BC).generateCertificate(getClass().getResourceAsStream(name));
+ }
+
+ private PrivateKey loadKey(String name)
+ throws Exception
+ {
+ return new JcaPEMKeyConverter().setProvider("BC").getKeyPair((PEMKeyPair)(new PEMParser(new InputStreamReader(getClass().getResourceAsStream(name)))).readObject()).getPrivate();
+ }
+
+ public void testHeaders()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+
+ SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content
+ // we want encrypted.
+ //
+
+ MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider(BC).build());
+
+ assertEquals("application/pkcs7-mime; name=\"smime.p7m\"; smime-type=authEnveloped-data", mp.getHeader("Content-Type")[0]);
+ assertEquals("attachment; filename=\"smime.p7m\"", mp.getHeader("Content-Disposition")[0]);
+ assertEquals("S/MIME Encrypted Message", mp.getHeader("Content-Description")[0]);
+ }
+
+ public void testAES128Encrypted()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+ String algorithm = SMIMEAuthEnvelopedGenerator.AES256_GCM;
+
+ verifyAlgorithm(algorithm, msg);
+ }
+
+ public void testAES192Encrypted()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+ String algorithm = SMIMEAuthEnvelopedGenerator.AES256_GCM;
+
+ verifyAlgorithm(algorithm, msg);
+ }
+
+ public void testAES256Encrypted()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+ String algorithm = SMIMEAuthEnvelopedGenerator.AES256_GCM;
+
+ verifyAlgorithm(algorithm, msg);
+ }
+
+ public void testSubKeyId()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+
+ SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
+
+ //
+ // create a subject key id - this has to be done the same way as
+ // it is done in the certificate associated with the private key
+ //
+ MessageDigest dig = MessageDigest.getInstance("SHA1", BC);
+ dig.update(SubjectPublicKeyInfo.getInstance(_reciCert.getPublicKey().getEncoded()).getPublicKeyData().getBytes());
+
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(dig.digest(), _reciCert.getPublicKey()).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content
+ // we want encrypted.
+ //
+
+ MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider(BC).build());
+
+ SMIMEEnveloped m = new SMIMEEnveloped(mp);
+
+ dig.update(SubjectPublicKeyInfo.getInstance(_reciCert.getPublicKey().getEncoded()).getPublicKeyData().getBytes());
+
+ RecipientId recId = new KeyTransRecipientId(dig.digest());
+
+ RecipientInformationStore recipients = m.getRecipientInfos();
+ RecipientInformation recipient = recipients.get(recId);
+
+ MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(msg, res);
+ }
+
+ public void testDotNetEncMailMatch()
+ throws Exception
+ {
+ MimeMessage message = loadMessage("dotnet_encrypted_mail.eml");
+
+ SMIMEEnveloped env = new SMIMEEnveloped(message);
+
+ RecipientInformationStore store = env.getRecipientInfos();
+
+ assertNotNull(store.get(new JceKeyTransRecipientId(loadCert("dotnet_enc_cert.pem"))));
+ }
+
+ public void testAES128()
+ throws Exception
+ {
+ MimeMessage message = loadMessage("test128.message");
+
+ SMIMEEnveloped env = new SMIMEEnveloped(message);
+
+ RecipientInformationStore store = env.getRecipientInfos();
+
+ RecipientInformation recipInfo = store.get(new JceKeyTransRecipientId(loadCert("cert.pem")));
+
+ assertNotNull(recipInfo);
+
+ byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(loadKey("key.pem")));
+
+ assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+ }
+
+ public void testAES192()
+ throws Exception
+ {
+ MimeMessage message = loadMessage("test192.message");
+
+ SMIMEEnveloped env = new SMIMEEnveloped(message);
+
+ RecipientInformationStore store = env.getRecipientInfos();
+
+ RecipientInformation recipInfo = store.get(new JceKeyTransRecipientId(loadCert("cert.pem")));
+
+ assertNotNull(recipInfo);
+
+ byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(loadKey("key.pem")));
+
+ assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+ }
+
+ public void testAES256()
+ throws Exception
+ {
+ MimeMessage message = loadMessage("test256.message");
+
+ SMIMEEnveloped env = new SMIMEEnveloped(message);
+
+ RecipientInformationStore store = env.getRecipientInfos();
+
+ RecipientInformation recipInfo = store.get(new JceKeyTransRecipientId(loadCert("cert.pem")));
+
+ assertNotNull(recipInfo);
+
+ byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(loadKey("key.pem")));
+
+ assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+ }
+
+ public void testCapEncrypt()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+
+ SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
+
+ //
+ // create a subject key id - this has to be done the same way as
+ // it is done in the certificate associated with the private key
+ //
+ MessageDigest dig = MessageDigest.getInstance("SHA256", BC);
+
+ dig.update(_reciCert.getPublicKey().getEncoded());
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(dig.digest(), _reciCert.getPublicKey()).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content
+ // we want encrypted.
+ //
+ MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider(BC).build());
+
+ SMIMEAuthEnveloped m = new SMIMEAuthEnveloped(mp);
+
+ dig.update(_reciCert.getPublicKey().getEncoded());
+
+ RecipientId recId = new KeyTransRecipientId(dig.digest());
+
+ RecipientInformationStore recipients = m.getRecipientInfos();
+ RecipientInformation recipient = recipients.get(recId);
+
+ MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(msg, res);
+ }
+
+ public void testTwoRecipients()
+ throws Exception
+ {
+ MimeBodyPart _msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+
+ SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert2).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content we want encrypted.
+ //
+ MimeBodyPart mp = gen.generate(_msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider(BC).build());
+
+ SMIMEAuthEnvelopedParser m = new SMIMEAuthEnvelopedParser(mp);
+
+ RecipientId recId = getRecipientId(_reciCert2);
+
+ RecipientInformationStore recipients = m.getRecipientInfos();
+ RecipientInformation recipient = recipients.get(recId);
+
+ FileBackedMimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransAuthEnvelopedRecipient(_reciKP2.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(_msg, res);
+
+ mp = gen.generate(_msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider(BC).build());
+ m = new SMIMEAuthEnvelopedParser(mp);
+
+ res.dispose();
+
+ recId = getRecipientId(_reciCert);
+
+ recipients = m.getRecipientInfos();
+ recipient = recipients.get(recId);
+
+ res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(_msg, res);
+
+ res.dispose();
+ }
+
+ private void verifyAlgorithm(
+ String algorithmOid,
+ MimeBodyPart msg)
+ throws Exception
+ {
+ SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content
+ // we want encrypted.
+ //
+
+ MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(algorithmOid)).setProvider(BC).build());
+ SMIMEAuthEnveloped m = new SMIMEAuthEnveloped(mp);
+ RecipientId recId = getRecipientId(_reciCert);
+
+ RecipientInformationStore recipients = m.getRecipientInfos();
+ RecipientInformation recipient = recipients.get(recId);
+
+ MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(msg, res);
+ }
+
+ private void verifyParserAlgorithm(
+ String algorithmOid,
+ MimeBodyPart msg)
+ throws Exception
+ {
+ SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
+
+ gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+ //
+ // generate a MimeBodyPart object which encapsulates the content
+ // we want encrypted.
+ //
+
+ MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(algorithmOid)).setProvider(BC).build());
+ SMIMEAuthEnvelopedParser m = new SMIMEAuthEnvelopedParser(mp);
+ RecipientId recId = getRecipientId(_reciCert);
+
+ RecipientInformationStore recipients = m.getRecipientInfos();
+ RecipientInformation recipient = recipients.get(recId);
+
+ MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
+
+ SMIMETestUtil.verifyMessageBytes(msg, res);
+ }
+
+ private RecipientId getRecipientId(
+ X509Certificate cert)
+ throws IOException, CertificateEncodingException
+ {
+ RecipientId recId = new JceKeyTransRecipientId(cert);
+
+ return recId;
+ }
+
+ public void testKDFAgreements()
+ throws Exception
+ {
+ MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
+
+ doTryAgreement(msg, CMSAlgorithm.ECDH_SHA1KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECDH_SHA224KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECDH_SHA256KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECDH_SHA384KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECDH_SHA512KDF);
+
+ doTryAgreement(msg, CMSAlgorithm.ECCDH_SHA1KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECCDH_SHA224KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECCDH_SHA256KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECCDH_SHA384KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECCDH_SHA512KDF);
+
+ doTryAgreement(msg, CMSAlgorithm.ECMQV_SHA1KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECMQV_SHA224KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECMQV_SHA256KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECMQV_SHA384KDF);
+ doTryAgreement(msg, CMSAlgorithm.ECMQV_SHA512KDF);
+ }
+
+ private void doTryAgreement(MimeBodyPart data, ASN1ObjectIdentifier algorithm)
+ throws Exception
+ {
+ SMIMEAuthEnvelopedGenerator edGen = new SMIMEAuthEnvelopedGenerator();
+
+ edGen.addRecipientInfoGenerator(new JceKeyAgreeRecipientInfoGenerator(algorithm,
+ _origEcKP.getPrivate(), _origEcKP.getPublic(),
+ CMSAlgorithm.AES128_WRAP).addRecipient(_reciEcCert).setProvider(BC));
+
+ MimeBodyPart res = edGen.generate(
+ data,
+ new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM).setProvider(BC).build());
+
+ SMIMEAuthEnveloped ed = new SMIMEAuthEnveloped(res);
+
+ assertEquals(ed.getEncryptionAlgOID(), CMSAuthEnvelopedDataGenerator.AES128_GCM);
+
+ RecipientInformationStore recipients = ed.getRecipientInfos();
+
+ confirmDataReceived(recipients, data, _reciEcCert, _reciEcKP.getPrivate(), BC);
+ confirmNumberRecipients(recipients, 1);
+ }
+
+ private static void confirmDataReceived(RecipientInformationStore recipients,
+ MimeBodyPart expectedData, X509Certificate reciCert, PrivateKey reciPrivKey, String provider)
+ throws Exception
+ {
+ RecipientId rid = new JceKeyAgreeRecipientId(reciCert);
+
+ RecipientInformation recipient = recipients.get(rid);
+ assertNotNull(recipient);
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ expectedData.writeTo(bOut);
+
+ byte[] actualData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciPrivKey).setProvider(provider));
+ assertEquals(true, Arrays.equals(bOut.toByteArray(), actualData));
+ }
+
+ private static void confirmNumberRecipients(RecipientInformationStore recipients, int count)
+ {
+ assertEquals(count, recipients.getRecipients().size());
+ }
+}
diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedData.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedData.java
index f1edc92993..322ef043ea 100644
--- a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedData.java
+++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedData.java
@@ -134,6 +134,15 @@ public byte[] getMAC()
recipientInfos, this.authEncAlg, secureReadable);
}
+
+ /**
+ * return the object identifier for the content encryption algorithm.
+ */
+ public String getEncryptionAlgOID()
+ {
+ return authEncAlg.getAlgorithm().getId();
+ }
+
/**
* Return the originator information associated with this message if present.
*
diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java
index 92f93a2f63..f7de40cf35 100644
--- a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java
+++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java
@@ -4,6 +4,7 @@
import java.util.List;
import org.bouncycastle.asn1.cms.OriginatorInfo;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
/**
* General class for generating a CMS enveloped-data message.
@@ -11,7 +12,9 @@
public class CMSAuthEnvelopedGenerator
extends CMSEnvelopedGenerator
{
- final List recipientInfoGenerators = new ArrayList();
+ public static final String AES128_GCM = NISTObjectIdentifiers.id_aes128_GCM.getId();
+ public static final String AES192_GCM = NISTObjectIdentifiers.id_aes192_GCM.getId();
+ public static final String AES256_GCM = NISTObjectIdentifiers.id_aes256_GCM.getId();
protected CMSAttributeTableGenerator authAttrsGenerator = null;
protected CMSAttributeTableGenerator unauthAttrsGenerator = null;