Skip to content

Commit

Permalink
Merge branch 'mail-smime-auth-enveloped' of https://github.com/bukka/…
Browse files Browse the repository at this point in the history
…bc-java into bukka-mail-smime-auth-enveloped
  • Loading branch information
dghgit committed Dec 31, 2024
2 parents b4fe683 + 79999fe commit 1f3c4d6
Show file tree
Hide file tree
Showing 8 changed files with 842 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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.
*
* <pre>
* SMIMEAuthEnvelopedGenerator fact = new SMIMEAuthEnvelopedGenerator();
*
* fact.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
*
* MimeBodyPart mp = fact.generate(content, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_GCM).setProvider("BC").build());
* </pre>
*
* <b>Note:</b> 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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -249,7 +263,7 @@ OutputStream regenerate(
}
}

private static class WrappingIOException
protected static class WrappingIOException
extends IOException
{
private Throwable cause;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 1f3c4d6

Please sign in to comment.