org.bouncycastle.mime.smime.SMIMEEnvelopedWriter Maven / Gradle / Ivy
package org.bouncycastle.mime.smime;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import org.bouncycastle.cms.CMSAttributeTableGenerator;
import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.OriginatorInformation;
import org.bouncycastle.cms.RecipientInfoGenerator;
import org.bouncycastle.mime.Headers;
import org.bouncycastle.mime.MimeIOException;
import org.bouncycastle.mime.MimeWriter;
import org.bouncycastle.mime.encoding.Base64OutputStream;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.util.Strings;
/**
* Writer for SMIME Enveloped objects.
*/
public class SMIMEEnvelopedWriter
extends MimeWriter
{
public static class Builder
{
private static final String[] stdHeaders;
private static final String[] stdValues;
static
{
stdHeaders = new String[]
{
"Content-Type",
"Content-Disposition",
"Content-Transfer-Encoding",
"Content-Description"
};
stdValues = new String[]
{
"application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data",
"attachment; filename=\"smime.p7m\"",
"base64",
"S/MIME Encrypted Message"
};
}
private final CMSEnvelopedDataStreamGenerator envGen = new CMSEnvelopedDataStreamGenerator();
private final Map headers = new LinkedHashMap();
String contentTransferEncoding = "base64";
public Builder()
{
for (int i = 0; i != stdHeaders.length; i++)
{
headers.put(stdHeaders[i], stdValues[i]);
}
}
/**
* Set the underlying string size for encapsulated data
*
* @param bufferSize length of octet strings to buffer the data.
*/
public Builder setBufferSize(
int bufferSize)
{
this.envGen.setBufferSize(bufferSize);
return this;
}
public Builder setUnprotectedAttributeGenerator(CMSAttributeTableGenerator unprotectedAttributeGenerator)
{
this.envGen.setUnprotectedAttributeGenerator(unprotectedAttributeGenerator);
return this;
}
public Builder setOriginatorInfo(OriginatorInformation originatorInfo)
{
this.envGen.setOriginatorInfo(originatorInfo);
return this;
}
/**
* Specify a MIME header (name, value) pair for this builder. If the headerName already exists it will
* be overridden.
*
* @param headerName name of the MIME header.
* @param headerValue value of the MIME header.
*
* @return the current Builder instance.
*/
public Builder withHeader(String headerName, String headerValue)
{
this.headers.put(headerName, headerValue);
return this;
}
/**
* Add a generator to produce the recipient info required.
*
* @param recipientGenerator a generator of a recipient info object.
*
* @return the current Builder instance.
*/
public Builder addRecipientInfoGenerator(RecipientInfoGenerator recipientGenerator)
{
this.envGen.addRecipientInfoGenerator(recipientGenerator);
return this;
}
public SMIMEEnvelopedWriter build(OutputStream mimeOut, OutputEncryptor outEnc)
{
return new SMIMEEnvelopedWriter(this, outEnc, SMimeUtils.autoBuffer(mimeOut));
}
}
private final CMSEnvelopedDataStreamGenerator envGen;
private final OutputEncryptor outEnc;
private final OutputStream mimeOut;
private final String contentTransferEncoding;
private SMIMEEnvelopedWriter(Builder builder, OutputEncryptor outEnc, OutputStream mimeOut)
{
super(new Headers(mapToLines(builder.headers), builder.contentTransferEncoding));
this.envGen = builder.envGen;
this.contentTransferEncoding = builder.contentTransferEncoding;
this.outEnc = outEnc;
this.mimeOut = mimeOut;
}
public OutputStream getContentStream()
throws IOException
{
headers.dumpHeaders(mimeOut);
mimeOut.write(Strings.toByteArray("\r\n"));
try
{
OutputStream backing = mimeOut;
if ("base64".equals(contentTransferEncoding))
{
backing = new Base64OutputStream(backing);
}
OutputStream main = envGen.open(SMimeUtils.createUnclosable(backing), outEnc);
return new ContentOutputStream(main, backing);
}
catch (CMSException e)
{
throw new MimeIOException(e.getMessage(), e);
}
}
private class ContentOutputStream
extends OutputStream
{
private final OutputStream main;
private final OutputStream backing;
ContentOutputStream(OutputStream main, OutputStream backing)
{
this.main = main;
this.backing = backing;
}
public void write(byte[] buf)
throws IOException
{
main.write(buf);
}
public void write(byte[] buf, int off, int len)
throws IOException
{
main.write(buf, off, len);
}
public void write(int i)
throws IOException
{
main.write(i);
}
public void close()
throws IOException
{
main.close();
if (backing != null)
{
backing.close();
}
}
}
}