Add spongy castle sources to libraries folder

This commit is contained in:
Dominik Schürmann
2014-01-27 14:00:22 +01:00
parent 8ca42b9bf9
commit 5aec25ac05
4258 changed files with 848014 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
/**
* a holding class for a BodyPart to be processed.
*/
public class CMSProcessableBodyPart
implements CMSProcessable
{
private BodyPart bodyPart;
public CMSProcessableBodyPart(
BodyPart bodyPart)
{
this.bodyPart = bodyPart;
}
public void write(
OutputStream out)
throws IOException, CMSException
{
try
{
bodyPart.writeTo(out);
}
catch (MessagingException e)
{
throw new CMSException("can't write BodyPart to stream.", e);
}
}
public Object getContent()
{
return bodyPart;
}
}

View File

@@ -0,0 +1,66 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
/**
* a holding class for a BodyPart to be processed which does CRLF canonicalisation if
* dealing with non-binary data.
*/
public class CMSProcessableBodyPartInbound
implements CMSProcessable
{
private final BodyPart bodyPart;
private final String defaultContentTransferEncoding;
/**
* Create a processable with the default transfer encoding of 7bit
*
* @param bodyPart body part to be processed
*/
public CMSProcessableBodyPartInbound(
BodyPart bodyPart)
{
this(bodyPart, "7bit");
}
/**
* Create a processable with the a default transfer encoding of
* the passed in value.
*
* @param bodyPart body part to be processed
* @param defaultContentTransferEncoding the new default to use.
*/
public CMSProcessableBodyPartInbound(
BodyPart bodyPart,
String defaultContentTransferEncoding)
{
this.bodyPart = bodyPart;
this.defaultContentTransferEncoding = defaultContentTransferEncoding;
}
public void write(
OutputStream out)
throws IOException, CMSException
{
try
{
SMIMEUtil.outputBodyPart(out, bodyPart, defaultContentTransferEncoding);
}
catch (MessagingException e)
{
throw new CMSException("can't write BodyPart to stream: " + e, e);
}
}
public Object getContent()
{
return bodyPart;
}
}

View File

@@ -0,0 +1,73 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.mail.smime.util.CRLFOutputStream;
/**
* a holding class for a BodyPart to be processed which does CRLF canocicalisation if
* dealing with non-binary data.
*/
public class CMSProcessableBodyPartOutbound
implements CMSProcessable
{
private BodyPart bodyPart;
private String defaultContentTransferEncoding;
/**
* Create a processable with the default transfer encoding of 7bit
*
* @param bodyPart body part to be processed
*/
public CMSProcessableBodyPartOutbound(
BodyPart bodyPart)
{
this.bodyPart = bodyPart;
}
/**
* Create a processable with the a default transfer encoding of
* the passed in value.
*
* @param bodyPart body part to be processed
* @param defaultContentTransferEncoding the new default to use.
*/
public CMSProcessableBodyPartOutbound(
BodyPart bodyPart,
String defaultContentTransferEncoding)
{
this.bodyPart = bodyPart;
this.defaultContentTransferEncoding = defaultContentTransferEncoding;
}
public void write(
OutputStream out)
throws IOException, CMSException
{
try
{
if (SMIMEUtil.isCanonicalisationRequired((MimeBodyPart)bodyPart, defaultContentTransferEncoding))
{
out = new CRLFOutputStream(out);
}
bodyPart.writeTo(out);
}
catch (MessagingException e)
{
throw new CMSException("can't write BodyPart to stream.", e);
}
}
public Object getContent()
{
return bodyPart;
}
}

View File

@@ -0,0 +1,59 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.InputStream;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimePart;
import org.bouncycastle.cms.CMSCompressedData;
import org.bouncycastle.cms.CMSException;
/**
* containing class for an S/MIME pkcs7-mime MimePart.
*/
public class SMIMECompressed
extends CMSCompressedData
{
MimePart message;
private static InputStream getInputStream(
Part bodyPart)
throws MessagingException
{
try
{
return bodyPart.getInputStream();
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
public SMIMECompressed(
MimeBodyPart message)
throws MessagingException, CMSException
{
super(getInputStream(message));
this.message = message;
}
public SMIMECompressed(
MimeMessage message)
throws MessagingException, CMSException
{
super(getInputStream(message));
this.message = message;
}
public MimePart getCompressedContent()
{
return message;
}
}

View File

@@ -0,0 +1,142 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.CMSCompressedDataGenerator;
import org.bouncycastle.cms.CMSCompressedDataStreamGenerator;
import org.bouncycastle.operator.OutputCompressor;
/**
* General class for generating a pkcs7-mime compressed message.
*
* A simple example of usage.
*
* <pre>
* SMIMECompressedGenerator fact = new SMIMECompressedGenerator();
*
* MimeBodyPart smime = fact.generate(content, algorithm);
* </pre>
*
* <b>Note:<b> Most clients expect the MimeBodyPart to be in a MimeMultipart
* when it's sent.
*/
public class SMIMECompressedGenerator
extends SMIMEGenerator
{
public static final String ZLIB = CMSCompressedDataGenerator.ZLIB;
private static final String COMPRESSED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7z\"; smime-type=compressed-data";
static
{
MailcapCommandMap mc = (MailcapCommandMap)CommandMap.getDefaultCommandMap();
mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
CommandMap.setDefaultCommandMap(mc);
}
/**
* generate an compressed object that contains an SMIME Compressed
* object using the given compression algorithm.
*/
private MimeBodyPart make(
MimeBodyPart content,
OutputCompressor compressor)
throws SMIMEException
{
try
{
MimeBodyPart data = new MimeBodyPart();
data.setContent(new ContentCompressor(content, compressor), COMPRESSED_CONTENT_TYPE);
data.addHeader("Content-Type", COMPRESSED_CONTENT_TYPE);
data.addHeader("Content-Disposition", "attachment; filename=\"smime.p7z\"");
data.addHeader("Content-Description", "S/MIME Compressed Message");
data.addHeader("Content-Transfer-Encoding", encoding);
return data;
}
catch (MessagingException e)
{
throw new SMIMEException("exception putting multi-part together.", e);
}
}
/**
* generate an compressed object that contains an SMIME Compressed
* object using the given provider from the contents of the passed in
* message
*/
public MimeBodyPart generate(
MimeBodyPart content,
OutputCompressor compressor)
throws SMIMEException
{
return make(makeContentBodyPart(content), compressor);
}
/**
* generate an compressed object that contains an SMIME Compressed
* object using the given provider from the contents of the passed in
* message
*/
public MimeBodyPart generate(
MimeMessage message,
OutputCompressor compressor)
throws SMIMEException
{
try
{
message.saveChanges(); // make sure we're up to date.
}
catch (MessagingException e)
{
throw new SMIMEException("unable to save message", e);
}
return make(makeContentBodyPart(message), compressor);
}
private class ContentCompressor
implements SMIMEStreamingProcessor
{
private final MimeBodyPart content;
private final OutputCompressor compressor;
ContentCompressor(
MimeBodyPart content,
OutputCompressor compressor)
{
this.content = content;
this.compressor = compressor;
}
public void write(OutputStream out)
throws IOException
{
CMSCompressedDataStreamGenerator cGen = new CMSCompressedDataStreamGenerator();
OutputStream compressed = cGen.open(out, compressor);
try
{
content.writeTo(compressed);
compressed.close();
}
catch (MessagingException e)
{
throw new IOException(e.toString());
}
}
}
}

View File

@@ -0,0 +1,100 @@
package org.bouncycastle.mail.smime;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimePart;
import org.bouncycastle.cms.CMSCompressedDataParser;
import org.bouncycastle.cms.CMSException;
/**
* Stream based containing class for an S/MIME pkcs7-mime compressed MimePart.
*/
public class SMIMECompressedParser
extends CMSCompressedDataParser
{
private final MimePart message;
private static InputStream getInputStream(
Part bodyPart,
int bufferSize)
throws MessagingException
{
try
{
InputStream in = bodyPart.getInputStream();
if (bufferSize == 0)
{
return new BufferedInputStream(in);
}
else
{
return new BufferedInputStream(in, bufferSize);
}
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
public SMIMECompressedParser(
MimeBodyPart message)
throws MessagingException, CMSException
{
this(message, 0);
}
public SMIMECompressedParser(
MimeMessage message)
throws 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 SMIMECompressedParser(
MimeBodyPart message,
int bufferSize)
throws MessagingException, CMSException
{
super(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 bufferSoze to be used.
*/
public SMIMECompressedParser(
MimeMessage message,
int bufferSize)
throws MessagingException, CMSException
{
super(getInputStream(message, bufferSize));
this.message = message;
}
public MimePart getCompressedContent()
{
return message;
}
}

View File

@@ -0,0 +1,59 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.InputStream;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimePart;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
/**
* containing class for an S/MIME pkcs7-mime encrypted MimePart.
*/
public class SMIMEEnveloped
extends CMSEnvelopedData
{
MimePart message;
private static InputStream getInputStream(
Part bodyPart)
throws MessagingException
{
try
{
return bodyPart.getInputStream();
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
public SMIMEEnveloped(
MimeBodyPart message)
throws MessagingException, CMSException
{
super(getInputStream(message));
this.message = message;
}
public SMIMEEnveloped(
MimeMessage message)
throws MessagingException, CMSException
{
super(getInputStream(message));
this.message = message;
}
public MimePart getEncryptedContent()
{
return message;
}
}

View File

@@ -0,0 +1,272 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientInfoGenerator;
import org.bouncycastle.operator.OutputEncryptor;
/**
* General class for generating a pkcs7-mime message.
*
* A simple example of usage.
*
* <pre>
* SMIMEEnvelopedGenerator fact = new SMIMEEnvelopedGenerator();
*
* fact.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(recipientCert).setProvider("BC"));
*
* MimeBodyPart mp = fact.generate(content, new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC, 40).setProvider("BC").build());
* </pre>
*
* <b>Note:<b> Most clients expect the MimeBodyPart to be in a MimeMultipart
* when it's sent.
*/
public class SMIMEEnvelopedGenerator
extends SMIMEGenerator
{
public static final String DES_EDE3_CBC = CMSEnvelopedDataGenerator.DES_EDE3_CBC;
public static final String RC2_CBC = CMSEnvelopedDataGenerator.RC2_CBC;
public static final String IDEA_CBC = CMSEnvelopedDataGenerator.IDEA_CBC;
public static final String CAST5_CBC = CMSEnvelopedDataGenerator.CAST5_CBC;
public static final String AES128_CBC = CMSEnvelopedDataGenerator.AES128_CBC;
public static final String AES192_CBC = CMSEnvelopedDataGenerator.AES192_CBC;
public static final String AES256_CBC = CMSEnvelopedDataGenerator.AES256_CBC;
public static final String CAMELLIA128_CBC = CMSEnvelopedDataGenerator.CAMELLIA128_CBC;
public static final String CAMELLIA192_CBC = CMSEnvelopedDataGenerator.CAMELLIA192_CBC;
public static final String CAMELLIA256_CBC = CMSEnvelopedDataGenerator.CAMELLIA256_CBC;
public static final String SEED_CBC = CMSEnvelopedDataGenerator.SEED_CBC;
public static final String DES_EDE3_WRAP = CMSEnvelopedDataGenerator.DES_EDE3_WRAP;
public static final String AES128_WRAP = CMSEnvelopedDataGenerator.AES128_WRAP;
public static final String AES256_WRAP = CMSEnvelopedDataGenerator.AES256_WRAP;
public static final String CAMELLIA128_WRAP = CMSEnvelopedDataGenerator.CAMELLIA128_WRAP;
public static final String CAMELLIA192_WRAP = CMSEnvelopedDataGenerator.CAMELLIA192_WRAP;
public static final String CAMELLIA256_WRAP = CMSEnvelopedDataGenerator.CAMELLIA256_WRAP;
public static final String SEED_WRAP = CMSEnvelopedDataGenerator.SEED_WRAP;
public static final String ECDH_SHA1KDF = CMSEnvelopedDataGenerator.ECDH_SHA1KDF;
private static final String ENCRYPTED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data";
private EnvelopedGenerator fact;
private List recipients = new ArrayList();
static
{
CommandMap.setDefaultCommandMap(addCommands(CommandMap.getDefaultCommandMap()));
}
private static MailcapCommandMap addCommands(CommandMap cm)
{
MailcapCommandMap mc = (MailcapCommandMap)cm;
mc.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mc.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
mc.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
return mc;
}
/**
* base constructor
*/
public SMIMEEnvelopedGenerator()
{
fact = new EnvelopedGenerator();
}
/**
* add a recipientInfoGenerator.
*/
public void addRecipientInfoGenerator(
RecipientInfoGenerator recipientInfoGen)
throws IllegalArgumentException
{
fact.addRecipientInfoGenerator(recipientInfoGen);
}
/**
* Use a BER Set to store the recipient information
*/
public void setBerEncodeRecipients(
boolean berEncodeRecipientSet)
{
fact.setBEREncodeRecipients(berEncodeRecipientSet);
}
/**
* if we get here we expect the Mime body part to be well defined.
*/
private MimeBodyPart make(
MimeBodyPart content,
OutputEncryptor encryptor)
throws SMIMEException
{
try
{
MimeBodyPart data = new MimeBodyPart();
data.setContent(new ContentEncryptor(content, encryptor), ENCRYPTED_CONTENT_TYPE);
data.addHeader("Content-Type", ENCRYPTED_CONTENT_TYPE);
data.addHeader("Content-Disposition", "attachment; filename=\"smime.p7m\"");
data.addHeader("Content-Description", "S/MIME Encrypted Message");
data.addHeader("Content-Transfer-Encoding", encoding);
return data;
}
catch (MessagingException e)
{
throw new SMIMEException("exception putting multi-part together.", e);
}
}
/**
* generate an enveloped object that contains an SMIME Enveloped
* object using the given content encryptor
*/
public MimeBodyPart generate(
MimeBodyPart content,
OutputEncryptor encryptor)
throws SMIMEException
{
return make(makeContentBodyPart(content), encryptor);
}
/**
* generate an enveloped object that contains an SMIME Enveloped
* object using the given provider from the contents of the passed in
* message
*/
public MimeBodyPart generate(
MimeMessage message,
OutputEncryptor encryptor)
throws SMIMEException
{
try
{
message.saveChanges(); // make sure we're up to date.
}
catch (MessagingException e)
{
throw new SMIMEException("unable to save message", e);
}
return make(makeContentBodyPart(message), encryptor);
}
private class ContentEncryptor
implements SMIMEStreamingProcessor
{
private final MimeBodyPart _content;
private OutputEncryptor _encryptor;
private boolean _firstTime = true;
ContentEncryptor(
MimeBodyPart content,
OutputEncryptor encryptor)
{
_content = content;
_encryptor = encryptor;
}
public void write(OutputStream out)
throws IOException
{
OutputStream encrypted;
try
{
if (_firstTime)
{
encrypted = fact.open(out, _encryptor);
_firstTime = false;
}
else
{
encrypted = fact.regenerate(out, _encryptor);
}
_content.getDataHandler().setCommandMap(addCommands(CommandMap.getDefaultCommandMap()));
_content.writeTo(encrypted);
encrypted.close();
}
catch (MessagingException e)
{
throw new WrappingIOException(e.toString(), e);
}
catch (CMSException e)
{
throw new WrappingIOException(e.toString(), e);
}
}
}
private class EnvelopedGenerator
extends CMSEnvelopedDataStreamGenerator
{
private ASN1ObjectIdentifier dataType;
private ASN1EncodableVector recipientInfos;
protected OutputStream open(
ASN1ObjectIdentifier dataType,
OutputStream out,
ASN1EncodableVector recipientInfos,
OutputEncryptor encryptor)
throws IOException
{
this.dataType = dataType;
this.recipientInfos = recipientInfos;
return super.open(dataType, out, recipientInfos, encryptor);
}
OutputStream regenerate(
OutputStream out,
OutputEncryptor encryptor)
throws IOException
{
return super.open(dataType, out, recipientInfos, encryptor);
}
}
private static class WrappingIOException
extends IOException
{
private Throwable cause;
WrappingIOException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}
}

View File

@@ -0,0 +1,100 @@
package org.bouncycastle.mail.smime;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimePart;
import org.bouncycastle.cms.CMSEnvelopedDataParser;
import org.bouncycastle.cms.CMSException;
/**
* Stream based containing class for an S/MIME pkcs7-mime encrypted MimePart.
*/
public class SMIMEEnvelopedParser
extends CMSEnvelopedDataParser
{
private final MimePart message;
private static InputStream getInputStream(
Part bodyPart,
int bufferSize)
throws MessagingException
{
try
{
InputStream in = bodyPart.getInputStream();
if (bufferSize == 0)
{
return new BufferedInputStream(in);
}
else
{
return new BufferedInputStream(in, bufferSize);
}
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
public SMIMEEnvelopedParser(
MimeBodyPart message)
throws IOException, MessagingException, CMSException
{
this(message, 0);
}
public SMIMEEnvelopedParser(
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 SMIMEEnvelopedParser(
MimeBodyPart message,
int bufferSize)
throws IOException, MessagingException, CMSException
{
super(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 bufferSoze to be used.
*/
public SMIMEEnvelopedParser(
MimeMessage message,
int bufferSize)
throws IOException, MessagingException, CMSException
{
super(getInputStream(message, bufferSize));
this.message = message;
}
public MimePart getEncryptedContent()
{
return message;
}
}

View File

@@ -0,0 +1,32 @@
package org.bouncycastle.mail.smime;
public class SMIMEException
extends Exception
{
Exception e;
public SMIMEException(
String name)
{
super(name);
}
public SMIMEException(
String name,
Exception e)
{
super(name);
this.e = e;
}
public Exception getUnderlyingException()
{
return e;
}
public Throwable getCause()
{
return e;
}
}

View File

@@ -0,0 +1,223 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.KeyGenerator;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.CMSEnvelopedGenerator;
import org.bouncycastle.util.Strings;
/**
* super class of the various generators.
*/
public class SMIMEGenerator
{
private static Map BASE_CIPHER_NAMES = new HashMap();
static
{
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.DES_EDE3_CBC, "DESEDE");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES128_CBC, "AES");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES192_CBC, "AES");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES256_CBC, "AES");
}
protected boolean useBase64 = true;
protected String encoding = "base64"; // default sets base64
/**
* base constructor
*/
protected SMIMEGenerator()
{
}
/**
* set the content-transfer-encoding for the CMS block (enveloped data, signature, etc...) in the message.
*
* @param encoding the encoding to use, default "base64", use "binary" for a binary encoding.
*/
public void setContentTransferEncoding(
String encoding)
{
this.encoding = encoding;
this.useBase64 = Strings.toLowerCase(encoding).equals("base64");
}
/**
* Make sure we have a valid content body part - setting the headers
* with defaults if neccessary.
*/
protected MimeBodyPart makeContentBodyPart(
MimeBodyPart content)
throws SMIMEException
{
//
// add the headers to the body part - if they are missing, in
// the event they have already been set the content settings override
// any defaults that might be set.
//
try
{
MimeMessage msg = new MimeMessage((Session)null);
Enumeration e = content.getAllHeaders();
msg.setDataHandler(content.getDataHandler());
while (e.hasMoreElements())
{
Header hdr =(Header)e.nextElement();
msg.setHeader(hdr.getName(), hdr.getValue());
}
msg.saveChanges();
//
// we do this to make sure at least the default headers are
// set in the body part.
//
e = msg.getAllHeaders();
while (e.hasMoreElements())
{
Header hdr =(Header)e.nextElement();
if (Strings.toLowerCase(hdr.getName()).startsWith("content-"))
{
content.setHeader(hdr.getName(), hdr.getValue());
}
}
}
catch (MessagingException e)
{
throw new SMIMEException("exception saving message state.", e);
}
return content;
}
/**
* extract an appropriate body part from the passed in MimeMessage
*/
protected MimeBodyPart makeContentBodyPart(
MimeMessage message)
throws SMIMEException
{
MimeBodyPart content = new MimeBodyPart();
//
// add the headers to the body part.
//
try
{
message.removeHeader("Message-Id");
message.removeHeader("Mime-Version");
// JavaMail has a habit of reparsing some content types, if the bodypart is
// a multipart it might be signed, we rebuild the body part using the raw input stream for the message.
try
{
if (message.getContent() instanceof Multipart)
{
content.setContent(message.getRawInputStream(), message.getContentType());
extractHeaders(content, message);
return content;
}
}
catch (MessagingException e)
{
// fall back to usual method below
}
content.setContent(message.getContent(), message.getContentType());
content.setDataHandler(message.getDataHandler());
extractHeaders(content, message);
}
catch (MessagingException e)
{
throw new SMIMEException("exception saving message state.", e);
}
catch (IOException e)
{
throw new SMIMEException("exception getting message content.", e);
}
return content;
}
private void extractHeaders(MimeBodyPart content, MimeMessage message)
throws MessagingException
{
Enumeration e = message.getAllHeaders();
while (e.hasMoreElements())
{
Header hdr =(Header)e.nextElement();
content.addHeader(hdr.getName(), hdr.getValue());
}
}
protected KeyGenerator createSymmetricKeyGenerator(
String encryptionOID,
Provider provider)
throws NoSuchAlgorithmException
{
try
{
return createKeyGenerator(encryptionOID, provider);
}
catch (NoSuchAlgorithmException e)
{
try
{
String algName = (String)BASE_CIPHER_NAMES.get(encryptionOID);
if (algName != null)
{
return createKeyGenerator(algName, provider);
}
}
catch (NoSuchAlgorithmException ex)
{
// ignore
}
if (provider != null)
{
return createSymmetricKeyGenerator(encryptionOID, null);
}
throw e;
}
}
private KeyGenerator createKeyGenerator(
String algName,
Provider provider)
throws NoSuchAlgorithmException
{
if (provider != null)
{
return KeyGenerator.getInstance(algName, provider);
}
else
{
return KeyGenerator.getInstance(algName);
}
}
}

View File

@@ -0,0 +1,230 @@
package org.bouncycastle.mail.smime;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimePart;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* general class for handling a pkcs7-signature message.
* <p>
* A simple example of usage - note, in the example below the validity of
* the certificate isn't verified, just the fact that one of the certs
* matches the given signer...
* <p>
* <pre>
* CertStore certs = s.getCertificates("Collection", "BC");
* SignerInformationStore signers = s.getSignerInfos();
* Collection c = signers.getSigners();
* Iterator it = c.iterator();
*
* while (it.hasNext())
* {
* SignerInformation signer = (SignerInformation)it.next();
* Collection certCollection = certs.getCertificates(signer.getSID());
*
* Iterator certIt = certCollection.iterator();
* X509Certificate cert = (X509Certificate)certIt.next();
*
* if (signer.verify(cert.getPublicKey()))
* {
* verified++;
* }
* }
* </pre>
* <p>
* Note: if you are using this class with AS2 or some other protocol
* that does not use 7bit as the default content transfer encoding you
* will need to use the constructor that allows you to specify the default
* content transfer encoding, such as "binary".
* </p>
*/
public class SMIMESigned
extends CMSSignedData
{
Object message;
MimeBodyPart content;
private static InputStream getInputStream(
Part bodyPart)
throws MessagingException
{
try
{
if (bodyPart.isMimeType("multipart/signed"))
{
throw new MessagingException("attempt to create signed data object from multipart content - use MimeMultipart constructor.");
}
return bodyPart.getInputStream();
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
static
{
MailcapCommandMap mc = (MailcapCommandMap)CommandMap.getDefaultCommandMap();
mc.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mc.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
mc.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
CommandMap.setDefaultCommandMap(mc);
}
/**
* base constructor using a defaultContentTransferEncoding of 7bit
*
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.
*/
public SMIMESigned(
MimeMultipart message)
throws MessagingException, CMSException
{
super(new CMSProcessableBodyPartInbound(message.getBodyPart(0)), getInputStream(message.getBodyPart(1)));
this.message = message;
this.content = (MimeBodyPart)message.getBodyPart(0);
}
/**
* base constructor with settable contentTransferEncoding
*
* @param message the signed message
* @param defaultContentTransferEncoding new default to use
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.
*/
public SMIMESigned(
MimeMultipart message,
String defaultContentTransferEncoding)
throws MessagingException, CMSException
{
super(new CMSProcessableBodyPartInbound(message.getBodyPart(0), defaultContentTransferEncoding), getInputStream(message.getBodyPart(1)));
this.message = message;
this.content = (MimeBodyPart)message.getBodyPart(0);
}
/**
* base constructor for a signed message with encapsulated content.
*
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception SMIMEException if the body part encapsulated in the message cannot be extracted.
* @exception CMSException if some other problem occurs.
*/
public SMIMESigned(
Part message)
throws MessagingException, CMSException, SMIMEException
{
super(getInputStream(message));
this.message = message;
CMSProcessable cont = this.getSignedContent();
if (cont != null)
{
byte[] contBytes = (byte[])cont.getContent();
this.content = SMIMEUtil.toMimeBodyPart(contBytes);
}
}
/**
* return the content that was signed.
*/
public MimeBodyPart getContent()
{
return content;
}
/**
* Return the content that was signed as a mime message.
*
* @param session
* @return a MimeMessage holding the content.
* @throws MessagingException
*/
public MimeMessage getContentAsMimeMessage(Session session)
throws MessagingException, IOException
{
Object content = getSignedContent().getContent();
byte[] contentBytes = null;
if (content instanceof byte[])
{
contentBytes = (byte[])content;
}
else if (content instanceof MimePart)
{
MimePart part = (MimePart)content;
ByteArrayOutputStream out;
if (part.getSize() > 0)
{
out = new ByteArrayOutputStream(part.getSize());
}
else
{
out = new ByteArrayOutputStream();
}
part.writeTo(out);
contentBytes = out.toByteArray();
}
else
{
String type = "<null>";
if (content != null)
{
type = content.getClass().getName();
}
throw new MessagingException(
"Could not transfrom content of type "
+ type
+ " into MimeMessage.");
}
if (contentBytes != null)
{
ByteArrayInputStream in = new ByteArrayInputStream(contentBytes);
return new MimeMessage(session, in);
}
return null;
}
/**
* return the content that was signed - depending on whether this was
* unencapsulated or not it will return a MimeMultipart or a MimeBodyPart
*/
public Object getContentWithSignature()
{
return message;
}
}

View File

@@ -0,0 +1,606 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.mail.smime.util.CRLFOutputStream;
import org.bouncycastle.util.Store;
/**
* general class for generating a pkcs7-signature message.
* <p>
* A simple example of usage.
*
* <pre>
* X509Certificate signCert = ...
* KeyPair signKP = ...
*
* List certList = new ArrayList();
*
* certList.add(signCert);
*
* Store certs = new JcaCertStore(certList);
*
* SMIMESignedGenerator gen = new SMIMESignedGenerator();
*
* gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").build("SHA1withRSA", signKP.getPrivate(), signCert));
*
* gen.addCertificates(certs);
*
* MimeMultipart smime = fact.generate(content);
* </pre>
* <p>
* Note 1: if you are using this class with AS2 or some other protocol
* that does not use "7bit" as the default content transfer encoding you
* will need to use the constructor that allows you to specify the default
* content transfer encoding, such as "binary".
* </p>
* <p>
* Note 2: between RFC 3851 and RFC 5751 the values used in the micalg parameter
* for signed messages changed. We will accept both, but the default is now to use
* RFC 5751. In the event you are dealing with an older style system you will also need
* to use a constructor that sets the micalgs table and call it with RFC3851_MICALGS.
* </p>
*/
public class SMIMESignedGenerator
extends SMIMEGenerator
{
public static final String DIGEST_SHA1 = OIWObjectIdentifiers.idSHA1.getId();
public static final String DIGEST_MD5 = PKCSObjectIdentifiers.md5.getId();
public static final String DIGEST_SHA224 = NISTObjectIdentifiers.id_sha224.getId();
public static final String DIGEST_SHA256 = NISTObjectIdentifiers.id_sha256.getId();
public static final String DIGEST_SHA384 = NISTObjectIdentifiers.id_sha384.getId();
public static final String DIGEST_SHA512 = NISTObjectIdentifiers.id_sha512.getId();
public static final String DIGEST_GOST3411 = CryptoProObjectIdentifiers.gostR3411.getId();
public static final String DIGEST_RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.getId();
public static final String DIGEST_RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.getId();
public static final String DIGEST_RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256.getId();
public static final String ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption.getId();
public static final String ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1.getId();
public static final String ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
public static final String ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS.getId();
public static final String ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94.getId();
public static final String ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001.getId();
private static final String CERTIFICATE_MANAGEMENT_CONTENT = "application/pkcs7-mime; name=smime.p7c; smime-type=certs-only";
private static final String DETACHED_SIGNATURE_TYPE = "application/pkcs7-signature; name=smime.p7s; smime-type=signed-data";
private static final String ENCAPSULATED_SIGNED_CONTENT_TYPE = "application/pkcs7-mime; name=smime.p7m; smime-type=signed-data";
public static final Map RFC3851_MICALGS;
public static final Map RFC5751_MICALGS;
public static final Map STANDARD_MICALGS;
private static MailcapCommandMap addCommands(CommandMap cm)
{
MailcapCommandMap mc = (MailcapCommandMap)cm;
mc.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mc.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
mc.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
return mc;
}
static
{
CommandMap.setDefaultCommandMap(addCommands(CommandMap.getDefaultCommandMap()));
Map stdMicAlgs = new HashMap();
stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
RFC5751_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
Map oldMicAlgs = new HashMap();
oldMicAlgs.put(CMSAlgorithm.MD5, "md5");
oldMicAlgs.put(CMSAlgorithm.SHA1, "sha1");
oldMicAlgs.put(CMSAlgorithm.SHA224, "sha224");
oldMicAlgs.put(CMSAlgorithm.SHA256, "sha256");
oldMicAlgs.put(CMSAlgorithm.SHA384, "sha384");
oldMicAlgs.put(CMSAlgorithm.SHA512, "sha512");
oldMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
RFC3851_MICALGS = Collections.unmodifiableMap(oldMicAlgs);
STANDARD_MICALGS = RFC5751_MICALGS;
}
private final String defaultContentTransferEncoding;
private final Map micAlgs;
private List _certStores = new ArrayList();
private List certStores = new ArrayList();
private List crlStores = new ArrayList();
private List attrCertStores = new ArrayList();
private List signerInfoGens = new ArrayList();
private List _signers = new ArrayList();
private List _oldSigners = new ArrayList();
private List _attributeCerts = new ArrayList();
private Map _digests = new HashMap();
/**
* base constructor - default content transfer encoding 7bit
*/
public SMIMESignedGenerator()
{
this("7bit", STANDARD_MICALGS);
}
/**
* base constructor - default content transfer encoding explicitly set
*
* @param defaultContentTransferEncoding new default to use.
*/
public SMIMESignedGenerator(
String defaultContentTransferEncoding)
{
this(defaultContentTransferEncoding, STANDARD_MICALGS);
}
/**
* base constructor - default content transfer encoding explicitly set
*
* @param micAlgs a map of ANS1ObjectIdentifiers to strings hash algorithm names.
*/
public SMIMESignedGenerator(
Map micAlgs)
{
this("7bit", micAlgs);
}
/**
* base constructor - default content transfer encoding explicitly set
*
* @param defaultContentTransferEncoding new default to use.
* @param micAlgs a map of ANS1ObjectIdentifiers to strings hash algorithm names.
*/
public SMIMESignedGenerator(
String defaultContentTransferEncoding,
Map micAlgs)
{
this.defaultContentTransferEncoding = defaultContentTransferEncoding;
this.micAlgs = micAlgs;
}
/**
* Add a store of precalculated signers to the generator.
*
* @param signerStore store of signers
*/
public void addSigners(
SignerInformationStore signerStore)
{
Iterator it = signerStore.getSigners().iterator();
while (it.hasNext())
{
_oldSigners.add(it.next());
}
}
/**
*
* @param sigInfoGen
*/
public void addSignerInfoGenerator(SignerInfoGenerator sigInfoGen)
{
signerInfoGens.add(sigInfoGen);
}
public void addCertificates(
Store certStore)
{
certStores.add(certStore);
}
public void addCRLs(
Store crlStore)
{
crlStores.add(crlStore);
}
public void addAttributeCertificates(
Store certStore)
{
attrCertStores.add(certStore);
}
private void addHashHeader(
StringBuffer header,
List signers)
{
int count = 0;
//
// build the hash header
//
Iterator it = signers.iterator();
Set micAlgSet = new TreeSet();
while (it.hasNext())
{
Object signer = it.next();
ASN1ObjectIdentifier digestOID;
if (signer instanceof SignerInformation)
{
digestOID = ((SignerInformation)signer).getDigestAlgorithmID().getAlgorithm();
}
else
{
digestOID = ((SignerInfoGenerator)signer).getDigestAlgorithm().getAlgorithm();
}
String micAlg = (String)micAlgs.get(digestOID);
if (micAlg == null)
{
micAlgSet.add("unknown");
}
else
{
micAlgSet.add(micAlg);
}
}
it = micAlgSet.iterator();
while (it.hasNext())
{
String alg = (String)it.next();
if (count == 0)
{
if (micAlgSet.size() != 1)
{
header.append("; micalg=\"");
}
else
{
header.append("; micalg=");
}
}
else
{
header.append(',');
}
header.append(alg);
count++;
}
if (count != 0)
{
if (micAlgSet.size() != 1)
{
header.append('\"');
}
}
}
private MimeMultipart make(
MimeBodyPart content)
throws SMIMEException
{
try
{
MimeBodyPart sig = new MimeBodyPart();
sig.setContent(new ContentSigner(content, false), DETACHED_SIGNATURE_TYPE);
sig.addHeader("Content-Type", DETACHED_SIGNATURE_TYPE);
sig.addHeader("Content-Disposition", "attachment; filename=\"smime.p7s\"");
sig.addHeader("Content-Description", "S/MIME Cryptographic Signature");
sig.addHeader("Content-Transfer-Encoding", encoding);
//
// build the multipart header
//
StringBuffer header = new StringBuffer(
"signed; protocol=\"application/pkcs7-signature\"");
List allSigners = new ArrayList(_signers);
allSigners.addAll(_oldSigners);
allSigners.addAll(signerInfoGens);
addHashHeader(header, allSigners);
MimeMultipart mm = new MimeMultipart(header.toString());
mm.addBodyPart(content);
mm.addBodyPart(sig);
return mm;
}
catch (MessagingException e)
{
throw new SMIMEException("exception putting multi-part together.", e);
}
}
/*
* at this point we expect our body part to be well defined - generate with data in the signature
*/
private MimeBodyPart makeEncapsulated(
MimeBodyPart content)
throws SMIMEException
{
try
{
MimeBodyPart sig = new MimeBodyPart();
sig.setContent(new ContentSigner(content, true), ENCAPSULATED_SIGNED_CONTENT_TYPE);
sig.addHeader("Content-Type", ENCAPSULATED_SIGNED_CONTENT_TYPE);
sig.addHeader("Content-Disposition", "attachment; filename=\"smime.p7m\"");
sig.addHeader("Content-Description", "S/MIME Cryptographic Signed Data");
sig.addHeader("Content-Transfer-Encoding", encoding);
return sig;
}
catch (MessagingException e)
{
throw new SMIMEException("exception putting body part together.", e);
}
}
/**
* Return a map of oids and byte arrays representing the digests calculated on the content during
* the last generate.
*
* @return a map of oids (as String objects) and byte[] representing digests.
*/
public Map getGeneratedDigests()
{
return new HashMap(_digests);
}
public MimeMultipart generate(
MimeBodyPart content)
throws SMIMEException
{
return make(makeContentBodyPart(content));
}
public MimeMultipart generate(
MimeMessage message)
throws SMIMEException
{
try
{
message.saveChanges(); // make sure we're up to date.
}
catch (MessagingException e)
{
throw new SMIMEException("unable to save message", e);
}
return make(makeContentBodyPart(message));
}
/**
* generate a signed message with encapsulated content
* <p>
* Note: doing this is strongly <b>not</b> recommended as it means a
* recipient of the message will have to be able to read the signature to read the
* message.
*/
public MimeBodyPart generateEncapsulated(
MimeBodyPart content)
throws SMIMEException
{
return makeEncapsulated(makeContentBodyPart(content));
}
public MimeBodyPart generateEncapsulated(
MimeMessage message)
throws SMIMEException
{
try
{
message.saveChanges(); // make sure we're up to date.
}
catch (MessagingException e)
{
throw new SMIMEException("unable to save message", e);
}
return makeEncapsulated(makeContentBodyPart(message));
}
/**
* Creates a certificate management message which is like a signed message with no content
* or signers but that still carries certificates and CRLs.
*
* @return a MimeBodyPart containing the certs and CRLs.
*/
public MimeBodyPart generateCertificateManagement()
throws SMIMEException
{
try
{
MimeBodyPart sig = new MimeBodyPart();
sig.setContent(new ContentSigner(null, true), CERTIFICATE_MANAGEMENT_CONTENT);
sig.addHeader("Content-Type", CERTIFICATE_MANAGEMENT_CONTENT);
sig.addHeader("Content-Disposition", "attachment; filename=\"smime.p7c\"");
sig.addHeader("Content-Description", "S/MIME Certificate Management Message");
sig.addHeader("Content-Transfer-Encoding", encoding);
return sig;
}
catch (MessagingException e)
{
throw new SMIMEException("exception putting body part together.", e);
}
}
private class ContentSigner
implements SMIMEStreamingProcessor
{
private final MimeBodyPart content;
private final boolean encapsulate;
private final boolean noProvider;
ContentSigner(
MimeBodyPart content,
boolean encapsulate)
{
this.content = content;
this.encapsulate = encapsulate;
this.noProvider = true;
}
protected CMSSignedDataStreamGenerator getGenerator()
throws CMSException
{
CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
for (Iterator it = certStores.iterator(); it.hasNext();)
{
gen.addCertificates((Store)it.next());
}
for (Iterator it = crlStores.iterator(); it.hasNext();)
{
gen.addCRLs((Store)it.next());
}
for (Iterator it = attrCertStores.iterator(); it.hasNext();)
{
gen.addAttributeCertificates((Store)it.next());
}
for (Iterator it = signerInfoGens.iterator(); it.hasNext();)
{
gen.addSignerInfoGenerator((SignerInfoGenerator)it.next());
}
gen.addSigners(new SignerInformationStore(_oldSigners));
return gen;
}
private void writeBodyPart(
OutputStream out,
MimeBodyPart bodyPart)
throws IOException, MessagingException
{
if (bodyPart.getContent() instanceof Multipart)
{
Multipart mp = (Multipart)bodyPart.getContent();
ContentType contentType = new ContentType(mp.getContentType());
String boundary = "--" + contentType.getParameter("boundary");
SMIMEUtil.LineOutputStream lOut = new SMIMEUtil.LineOutputStream(out);
Enumeration headers = bodyPart.getAllHeaderLines();
while (headers.hasMoreElements())
{
lOut.writeln((String)headers.nextElement());
}
lOut.writeln(); // CRLF separator
SMIMEUtil.outputPreamble(lOut, bodyPart, boundary);
for (int i = 0; i < mp.getCount(); i++)
{
lOut.writeln(boundary);
writeBodyPart(out, (MimeBodyPart)mp.getBodyPart(i));
lOut.writeln(); // CRLF terminator
}
lOut.writeln(boundary + "--");
}
else
{
if (SMIMEUtil.isCanonicalisationRequired(bodyPart, defaultContentTransferEncoding))
{
out = new CRLFOutputStream(out);
}
bodyPart.writeTo(out);
}
}
public void write(OutputStream out)
throws IOException
{
try
{
CMSSignedDataStreamGenerator gen = getGenerator();
OutputStream signingStream = gen.open(out, encapsulate);
if (content != null)
{
if (!encapsulate)
{
writeBodyPart(signingStream, content);
}
else
{
content.getDataHandler().setCommandMap(addCommands(CommandMap.getDefaultCommandMap()));
content.writeTo(signingStream);
}
}
signingStream.close();
_digests = gen.getGeneratedDigests();
}
catch (MessagingException e)
{
throw new IOException(e.toString());
}
catch (CMSException e)
{
throw new IOException(e.toString());
}
}
}
}

View File

@@ -0,0 +1,358 @@
package org.bouncycastle.mail.smime;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedDataParser;
import org.bouncycastle.cms.CMSTypedStream;
import org.bouncycastle.operator.DigestCalculatorProvider;
/**
* general class for handling a pkcs7-signature message.
* <p>
* A simple example of usage - note, in the example below the validity of
* the certificate isn't verified, just the fact that one of the certs
* matches the given signer...
* <p>
* <pre>
* CertStore certs = s.getCertificates("Collection", "BC");
* SignerInformationStore signers = s.getSignerInfos();
* Collection c = signers.getSigners();
* Iterator it = c.iterator();
*
* while (it.hasNext())
* {
* SignerInformation signer = (SignerInformation)it.next();
* Collection certCollection = certs.getCertificates(signer.getSID());
*
* Iterator certIt = certCollection.iterator();
* X509Certificate cert = (X509Certificate)certIt.next();
*
* if (signer.verify(cert.getPublicKey()))
* {
* verified++;
* }
* }
* </pre>
* <p>
* Note: if you are using this class with AS2 or some other protocol
* that does not use 7bit as the default content transfer encoding you
* will need to use the constructor that allows you to specify the default
* content transfer encoding, such as "binary".
* </p>
*/
public class SMIMESignedParser
extends CMSSignedDataParser
{
Object message;
MimeBodyPart content;
private static InputStream getInputStream(
Part bodyPart)
throws MessagingException
{
try
{
if (bodyPart.isMimeType("multipart/signed"))
{
throw new MessagingException("attempt to create signed data object from multipart content - use MimeMultipart constructor.");
}
return bodyPart.getInputStream();
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
private static File getTmpFile()
throws MessagingException
{
try
{
return File.createTempFile("bcMail", ".mime");
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
private static CMSTypedStream getSignedInputStream(
BodyPart bodyPart,
String defaultContentTransferEncoding,
File backingFile)
throws MessagingException
{
try
{
OutputStream out = new BufferedOutputStream(new FileOutputStream(backingFile));
SMIMEUtil.outputBodyPart(out, bodyPart, defaultContentTransferEncoding);
out.close();
InputStream in = new TemporaryFileInputStream(backingFile);
return new CMSTypedStream(in);
}
catch (IOException e)
{
throw new MessagingException("can't extract input stream: " + e);
}
}
static
{
MailcapCommandMap mc = (MailcapCommandMap)CommandMap.getDefaultCommandMap();
mc.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mc.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
mc.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
CommandMap.setDefaultCommandMap(mc);
}
/**
* base constructor using a defaultContentTransferEncoding of 7bit. A temporary backing file
* will be created for the signed data.
*
* @param digCalcProvider provider for digest calculators.
* @param message signed message with signature.
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
MimeMultipart message)
throws MessagingException, CMSException
{
this(digCalcProvider, message, getTmpFile());
}
/**
* base constructor using a defaultContentTransferEncoding of 7bit and a specified backing file.
*
* @param digCalcProvider provider for digest calculators.
* @param message signed message with signature.
* @param backingFile the temporary file to use to back the signed data.
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
MimeMultipart message,
File backingFile)
throws MessagingException, CMSException
{
this(digCalcProvider, message, "7bit", backingFile);
}
/**
* base constructor with settable contentTransferEncoding. A temporary backing file will be created
* to contain the signed data.
*
* @param digCalcProvider provider for digest calculators.
* @param message the signed message with signature.
* @param defaultContentTransferEncoding new default to use.
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.r
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
MimeMultipart message,
String defaultContentTransferEncoding)
throws MessagingException, CMSException
{
this(digCalcProvider, message, defaultContentTransferEncoding, getTmpFile());
}
/**
* base constructor with settable contentTransferEncoding and a specified backing file.
*
* @param digCalcProvider provider for digest calculators.
* @param message the signed message with signature.
* @param defaultContentTransferEncoding new default to use.
* @param backingFile the temporary file to use to back the signed data.
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception CMSException if some other problem occurs.
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
MimeMultipart message,
String defaultContentTransferEncoding,
File backingFile)
throws MessagingException, CMSException
{
super(digCalcProvider, getSignedInputStream(message.getBodyPart(0), defaultContentTransferEncoding, backingFile), getInputStream(message.getBodyPart(1)));
this.message = message;
this.content = (MimeBodyPart)message.getBodyPart(0);
drainContent();
}
/**
* base constructor for a signed message with encapsulated content.
* <p>
* Note: in this case the encapsulated MimeBody part will only be suitable for a single
* writeTo - once writeTo has been called the file containing the body part will be deleted. If writeTo is not
* called the file will be left in the temp directory.
* </p>
* @param digCalcProvider provider for digest calculators.
* @param message the message containing the encapsulated signed data.
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception SMIMEException if the body part encapsulated in the message cannot be extracted.
* @exception CMSException if some other problem occurs.
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
Part message)
throws MessagingException, CMSException, SMIMEException
{
super(digCalcProvider, getInputStream(message));
this.message = message;
CMSTypedStream cont = this.getSignedContent();
if (cont != null)
{
this.content = SMIMEUtil.toWriteOnceBodyPart(cont);
}
}
/**
* Constructor for a signed message with encapsulated content. The encapsulated
* content, if it exists, is written to the file represented by the File object
* passed in.
*
* @param digCalcProvider provider for digest calculators.
* @param message the Part containing the signed content.
* @param file the file the encapsulated part is to be written to after it has been decoded.
*
* @exception MessagingException on an error extracting the signature or
* otherwise processing the message.
* @exception SMIMEException if the body part encapsulated in the message cannot be extracted.
* @exception CMSException if some other problem occurs.
*/
public SMIMESignedParser(
DigestCalculatorProvider digCalcProvider,
Part message,
File file)
throws MessagingException, CMSException, SMIMEException
{
super(digCalcProvider, getInputStream(message));
this.message = message;
CMSTypedStream cont = this.getSignedContent();
if (cont != null)
{
this.content = SMIMEUtil.toMimeBodyPart(cont, file);
}
}
/**
* return the content that was signed.
* @return the signed body part in this message.
*/
public MimeBodyPart getContent()
{
return content;
}
/**
* Return the content that was signed as a mime message.
*
* @param session the session to base the MimeMessage around.
* @return a MimeMessage holding the content.
* @throws MessagingException if there is an issue creating the MimeMessage.
* @throws IOException if there is an issue reading the content.
*/
public MimeMessage getContentAsMimeMessage(Session session)
throws MessagingException, IOException
{
if (message instanceof MimeMultipart)
{
BodyPart bp = ((MimeMultipart)message).getBodyPart(0);
return new MimeMessage(session, bp.getInputStream());
}
else
{
return new MimeMessage(session, getSignedContent().getContentStream());
}
}
/**
* return the content that was signed with its signature attached.
* @return depending on whether this was unencapsulated or not it will return a MimeMultipart
* or a MimeBodyPart
*/
public Object getContentWithSignature()
{
return message;
}
private void drainContent()
throws CMSException
{
try
{
this.getSignedContent().drain();
}
catch (IOException e)
{
throw new CMSException("unable to read content for verification: " + e, e);
}
}
private static class TemporaryFileInputStream
extends BufferedInputStream
{
private final File _file;
TemporaryFileInputStream(File file)
throws FileNotFoundException
{
super(new FileInputStream(file));
_file = file;
}
public void close()
throws IOException
{
super.close();
_file.delete();
}
}
}

View File

@@ -0,0 +1,10 @@
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.io.OutputStream;
public interface SMIMEStreamingProcessor
{
public void write(OutputStream out)
throws IOException;
}

View File

@@ -0,0 +1,623 @@
package org.bouncycastle.mail.smime;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.cms.CMSTypedStream;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.mail.smime.util.CRLFOutputStream;
import org.bouncycastle.mail.smime.util.FileBackedMimeBodyPart;
public class SMIMEUtil
{
private static final int BUF_SIZE = 32760;
static boolean isCanonicalisationRequired(
MimeBodyPart bodyPart,
String defaultContentTransferEncoding)
throws MessagingException
{
String[] cte = bodyPart.getHeader("Content-Transfer-Encoding");
String contentTransferEncoding;
if (cte == null)
{
contentTransferEncoding = defaultContentTransferEncoding;
}
else
{
contentTransferEncoding = cte[0];
}
return !contentTransferEncoding.equalsIgnoreCase("binary");
}
public static Provider getProvider(String providerName)
throws NoSuchProviderException
{
if (providerName != null)
{
Provider prov = Security.getProvider(providerName);
if (prov != null)
{
return prov;
}
throw new NoSuchProviderException("provider " + providerName + " not found.");
}
return null;
}
static class LineOutputStream extends FilterOutputStream
{
private static byte newline[];
public LineOutputStream(OutputStream outputstream)
{
super(outputstream);
}
public void writeln(String s)
throws MessagingException
{
try
{
byte abyte0[] = getBytes(s);
super.out.write(abyte0);
super.out.write(newline);
}
catch(Exception exception)
{
throw new MessagingException("IOException", exception);
}
}
public void writeln()
throws MessagingException
{
try
{
super.out.write(newline);
}
catch(Exception exception)
{
throw new MessagingException("IOException", exception);
}
}
static
{
newline = new byte[2];
newline[0] = 13;
newline[1] = 10;
}
private static byte[] getBytes(String s)
{
char ac[] = s.toCharArray();
int i = ac.length;
byte abyte0[] = new byte[i];
int j = 0;
while (j < i)
{
abyte0[j] = (byte)ac[j++];
}
return abyte0;
}
}
/**
* internal preamble is generally included in signatures, while this is technically wrong,
* if we find internal preamble we include it by default.
*/
static void outputPreamble(LineOutputStream lOut, MimeBodyPart part, String boundary)
throws MessagingException, IOException
{
InputStream in;
try
{
in = part.getRawInputStream();
}
catch (MessagingException e)
{
return; // no underlying content rely on default generation
}
String line;
while ((line = readLine(in)) != null)
{
if (line.equals(boundary))
{
break;
}
lOut.writeln(line);
}
in.close();
if (line == null)
{
throw new MessagingException("no boundary found");
}
}
/**
* internal postamble is generally included in signatures, while this is technically wrong,
* if we find internal postamble we include it by default.
*/
static void outputPostamble(LineOutputStream lOut, MimeBodyPart part, int count, String boundary)
throws MessagingException, IOException
{
InputStream in;
try
{
in = part.getRawInputStream();
}
catch (MessagingException e)
{
return; // no underlying content rely on default generation
}
String line;
int boundaries = count + 1;
while ((line = readLine(in)) != null)
{
if (line.startsWith(boundary))
{
boundaries--;
if (boundaries == 0)
{
break;
}
}
}
while ((line = readLine(in)) != null)
{
lOut.writeln(line);
}
in.close();
if (boundaries != 0)
{
throw new MessagingException("all boundaries not found for: " + boundary);
}
}
static void outputPostamble(LineOutputStream lOut, BodyPart parent, String parentBoundary, BodyPart part)
throws MessagingException, IOException
{
InputStream in;
try
{
in = ((MimeBodyPart)parent).getRawInputStream();
}
catch (MessagingException e)
{
return; // no underlying content rely on default generation
}
MimeMultipart multipart = (MimeMultipart)part.getContent();
ContentType contentType = new ContentType(multipart.getContentType());
String boundary = "--" + contentType.getParameter("boundary");
int count = multipart.getCount() + 1;
String line;
while (count != 0 && (line = readLine(in)) != null)
{
if (line.startsWith(boundary))
{
count--;
}
}
while ((line = readLine(in)) != null)
{
if (line.startsWith(parentBoundary))
{
break;
}
lOut.writeln(line);
}
in.close();
}
/*
* read a line of input stripping of the tailing \r\n
*/
private static String readLine(InputStream in)
throws IOException
{
StringBuffer b = new StringBuffer();
int ch;
while ((ch = in.read()) >= 0 && ch != '\n')
{
if (ch != '\r')
{
b.append((char)ch);
}
}
if (ch < 0 && b.length() == 0)
{
return null;
}
return b.toString();
}
static void outputBodyPart(
OutputStream out,
BodyPart bodyPart,
String defaultContentTransferEncoding)
throws MessagingException, IOException
{
if (bodyPart instanceof MimeBodyPart)
{
MimeBodyPart mimePart = (MimeBodyPart)bodyPart;
String[] cte = mimePart.getHeader("Content-Transfer-Encoding");
String contentTransferEncoding;
if (mimePart.getContent() instanceof MimeMultipart)
{
MimeMultipart mp = (MimeMultipart)bodyPart.getContent();
ContentType contentType = new ContentType(mp.getContentType());
String boundary = "--" + contentType.getParameter("boundary");
SMIMEUtil.LineOutputStream lOut = new SMIMEUtil.LineOutputStream(out);
Enumeration headers = mimePart.getAllHeaderLines();
while (headers.hasMoreElements())
{
String header = (String)headers.nextElement();
lOut.writeln(header);
}
lOut.writeln(); // CRLF separator
outputPreamble(lOut, mimePart, boundary);
for (int i = 0; i < mp.getCount(); i++)
{
lOut.writeln(boundary);
BodyPart part = mp.getBodyPart(i);
outputBodyPart(out, part, defaultContentTransferEncoding);
if (!(part.getContent() instanceof MimeMultipart))
{
lOut.writeln(); // CRLF terminator needed
}
else
{
outputPostamble(lOut, mimePart, boundary, part);
}
}
lOut.writeln(boundary + "--");
outputPostamble(lOut, mimePart, mp.getCount(), boundary);
return;
}
if (cte == null)
{
contentTransferEncoding = defaultContentTransferEncoding;
}
else
{
contentTransferEncoding = cte[0];
}
if (!contentTransferEncoding.equalsIgnoreCase("base64")
&& !contentTransferEncoding.equalsIgnoreCase("quoted-printable"))
{
if (!contentTransferEncoding.equalsIgnoreCase("binary"))
{
out = new CRLFOutputStream(out);
}
bodyPart.writeTo(out);
out.flush();
return;
}
boolean base64 = contentTransferEncoding.equalsIgnoreCase("base64");
//
// Write raw content, performing canonicalization
//
InputStream inRaw;
try
{
inRaw = mimePart.getRawInputStream();
}
catch (MessagingException e)
{
// this is less than ideal, but if the raw output stream is unavailable it's the
// best option we've got.
out = new CRLFOutputStream(out);
bodyPart.writeTo(out);
out.flush();
return;
}
//
// Write headers
//
LineOutputStream outLine = new LineOutputStream(out);
for (Enumeration e = mimePart.getAllHeaderLines(); e.hasMoreElements();)
{
String header = (String)e.nextElement();
outLine.writeln(header);
}
outLine.writeln();
outLine.flush();
OutputStream outCRLF;
if (base64)
{
outCRLF = new Base64CRLFOutputStream(out);
}
else
{
outCRLF = new CRLFOutputStream(out);
}
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = inRaw.read(buf, 0, buf.length)) > 0)
{
outCRLF.write(buf, 0, len);
}
outCRLF.flush();
}
else
{
if (!defaultContentTransferEncoding.equalsIgnoreCase("binary"))
{
out = new CRLFOutputStream(out);
}
bodyPart.writeTo(out);
out.flush();
}
}
/**
* return the MimeBodyPart described in the raw bytes provided in content
*/
public static MimeBodyPart toMimeBodyPart(
byte[] content)
throws SMIMEException
{
return toMimeBodyPart(new ByteArrayInputStream(content));
}
/**
* return the MimeBodyPart described in the input stream content
*/
public static MimeBodyPart toMimeBodyPart(
InputStream content)
throws SMIMEException
{
try
{
return new MimeBodyPart(content);
}
catch (MessagingException e)
{
throw new SMIMEException("exception creating body part.", e);
}
}
static FileBackedMimeBodyPart toWriteOnceBodyPart(
CMSTypedStream content)
throws SMIMEException
{
try
{
return new WriteOnceFileBackedMimeBodyPart(content.getContentStream(), File.createTempFile("bcMail", ".mime"));
}
catch (IOException e)
{
throw new SMIMEException("IOException creating tmp file:" + e.getMessage(), e);
}
catch (MessagingException e)
{
throw new SMIMEException("can't create part: " + e, e);
}
}
/**
* return a file backed MimeBodyPart described in {@link CMSTypedStream} content.
* </p>
*/
public static FileBackedMimeBodyPart toMimeBodyPart(
CMSTypedStream content)
throws SMIMEException
{
try
{
return toMimeBodyPart(content, File.createTempFile("bcMail", ".mime"));
}
catch (IOException e)
{
throw new SMIMEException("IOException creating tmp file:" + e.getMessage(), e);
}
}
/**
* Return a file based MimeBodyPart represented by content and backed
* by the file represented by file.
*
* @param content content stream containing body part.
* @param file file to store the decoded body part in.
* @return the decoded body part.
* @throws SMIMEException
*/
public static FileBackedMimeBodyPart toMimeBodyPart(
CMSTypedStream content,
File file)
throws SMIMEException
{
try
{
return new FileBackedMimeBodyPart(content.getContentStream(), file);
}
catch (IOException e)
{
throw new SMIMEException("can't save content to file: " + e, e);
}
catch (MessagingException e)
{
throw new SMIMEException("can't create part: " + e, e);
}
}
/**
* Return a CMS IssuerAndSerialNumber structure for the passed in X.509 certificate.
*
* @param cert the X.509 certificate to get the issuer and serial number for.
* @return an IssuerAndSerialNumber structure representing the certificate.
*/
public static IssuerAndSerialNumber createIssuerAndSerialNumberFor(
X509Certificate cert)
throws CertificateParsingException
{
try
{
return new IssuerAndSerialNumber(PrincipalUtil.getIssuerX509Principal(cert), cert.getSerialNumber());
}
catch (Exception e)
{
throw new CertificateParsingException("exception extracting issuer and serial number: " + e);
}
}
private static class WriteOnceFileBackedMimeBodyPart
extends FileBackedMimeBodyPart
{
public WriteOnceFileBackedMimeBodyPart(InputStream content, File file)
throws MessagingException, IOException
{
super(content, file);
}
public void writeTo(OutputStream out)
throws MessagingException, IOException
{
super.writeTo(out);
this.dispose();
}
}
static class Base64CRLFOutputStream extends FilterOutputStream
{
protected int lastb;
protected static byte newline[];
private boolean isCrlfStream;
public Base64CRLFOutputStream(OutputStream outputstream)
{
super(outputstream);
lastb = -1;
}
public void write(int i)
throws IOException
{
if (i == '\r')
{
out.write(newline);
}
else if (i == '\n')
{
if (lastb != '\r')
{ // imagine my joy...
if (!(isCrlfStream && lastb == '\n'))
{
out.write(newline);
}
}
else
{
isCrlfStream = true;
}
}
else
{
out.write(i);
}
lastb = i;
}
public void write(byte[] buf)
throws IOException
{
this.write(buf, 0, buf.length);
}
public void write(byte buf[], int off, int len)
throws IOException
{
for (int i = off; i != off + len; i++)
{
this.write(buf[i]);
}
}
public void writeln()
throws IOException
{
super.out.write(newline);
}
static
{
newline = new byte[2];
newline[0] = '\r';
newline[1] = '\n';
}
}
}

View File

@@ -0,0 +1,57 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileOutputStream;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.jcajce.ZlibCompressor;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
/**
* a simple example that creates a single compressed mail message.
*/
public class CreateCompressedMail
{
public static void main(
String args[])
throws Exception
{
//
// create the generator for creating an smime/compressed message
//
SMIMECompressedGenerator gen = new SMIMECompressedGenerator();
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setText("Hello world!");
MimeBodyPart mp = gen.generate(msg, new ZlibCompressor());
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example compressed message");
body.setContent(mp.getContent(), mp.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("compressed.message"));
}
}

View File

@@ -0,0 +1,121 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
/**
* a simple example that creates a single encrypted mail message.
* <p>
* The key store can be created using the class in
* org.bouncycastle.jce.examples.PKCS12Example - the program expects only one
* key to be present in the key file.
* <p>
* Note: while this means that both the private key is available to
* the program, the private key is retrieved from the keystore only for
* the purposes of locating the corresponding public key, in normal circumstances
* you would only be doing this with a certificate available.
*/
public class CreateEncryptedMail
{
public static void main(
String args[])
throws Exception
{
if (args.length != 2)
{
System.err.println("usage: CreateEncryptedMail pkcs12Keystore password");
System.exit(0);
}
//
// Open the key store
//
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
ks.load(new FileInputStream(args[0]), args[1].toCharArray());
Enumeration e = ks.aliases();
String keyAlias = null;
while (e.hasMoreElements())
{
String alias = (String)e.nextElement();
if (ks.isKeyEntry(alias))
{
keyAlias = alias;
}
}
if (keyAlias == null)
{
System.err.println("can't find a private key!");
System.exit(0);
}
Certificate[] chain = ks.getCertificateChain(keyAlias);
//
// create the generator for creating an smime/encrypted message
//
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC"));
//
// 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
// version 3 only.
//
/*
MessageDigest dig = MessageDigest.getInstance("SHA1", "BC");
dig.update(cert.getPublicKey().getEncoded());
gen.addKeyTransRecipient(cert.getPublicKey(), dig.digest());
*/
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setText("Hello world!");
MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build());
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example encrypted message");
body.setContent(mp.getContent(), mp.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("encrypted.message"));
}
}

View File

@@ -0,0 +1,63 @@
package org.bouncycastle.mail.smime.examples;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.jcajce.ZlibCompressor;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
/**
* a simple example that creates a single compressed mail message using the large
* file model.
*/
public class CreateLargeCompressedMail
{
public static void main(
String args[])
throws Exception
{
//
// create the generator for creating an smime/compressed message
//
SMIMECompressedGenerator gen = new SMIMECompressedGenerator();
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[0]))));
msg.setHeader("Content-Type", "application/octet-stream");
msg.setHeader("Content-Transfer-Encoding", "binary");
MimeBodyPart mp = gen.generate(msg, new ZlibCompressor());
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example compressed message");
body.setContent(mp.getContent(), mp.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("compressed.message"));
}
}

View File

@@ -0,0 +1,105 @@
package org.bouncycastle.mail.smime.examples;
import java.io.File;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
/**
* a simple example that creates a single encrypted mail message.
* <p>
* The key store can be created using the class in
* org.bouncycastle.jce.examples.PKCS12Example - the program expects only one
* key to be present in the key file.
* <p>
* Note: while this means that both the private key is available to
* the program, the private key is retrieved from the keystore only for
* the purposes of locating the corresponding public key, in normal circumstances
* you would only be doing this with a certificate available.
*/
public class CreateLargeEncryptedMail
{
public static void main(
String args[])
throws Exception
{
if (args.length != 3)
{
System.err.println("usage: CreateLargeEncryptedMail pkcs12Keystore password inputFile");
System.exit(0);
}
//
// Open the key store
//
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
String keyAlias = ExampleUtils.findKeyAlias(ks, args[0], args[1].toCharArray());
Certificate[] chain = ks.getCertificateChain(keyAlias);
//
// create the generator for creating an smime/encrypted message
//
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC"));
//
// 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
// version 3 only.
//
/*
MessageDigest dig = MessageDigest.getInstance("SHA1", "BC");
dig.update(cert.getPublicKey().getEncoded());
gen.addKeyTransRecipient(cert.getPublicKey(), dig.digest());
*/
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[2]))));
msg.setHeader("Content-Type", "application/octet-stream");
msg.setHeader("Content-Transfer-Encoding", "binary");
MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build());
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example encrypted message");
body.setContent(mp.getContent(), mp.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("encrypted.message"));
}
}

View File

@@ -0,0 +1,198 @@
package org.bouncycastle.mail.smime.examples;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.Store;
/**
* a simple example that creates a single signed mail message.
*/
public class CreateLargeSignedMail
{
//
// certificate serial number seed.
//
static int serialNo = 1;
/**
* create a basic X509 certificate from the given keys
*/
static X509Certificate makeCertificate(
KeyPair subKP,
String subDN,
KeyPair issKP,
String issDN)
throws GeneralSecurityException, IOException, OperatorCreationException
{
PublicKey subPub = subKP.getPublic();
PrivateKey issPriv = issKP.getPrivate();
PublicKey issPub = issKP.getPublic();
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN), BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);
v3CertGen.addExtension(
X509Extension.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(subPub));
v3CertGen.addExtension(
X509Extension.authorityKeyIdentifier,
false,
extUtils.createAuthorityKeyIdentifier(issPub));
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv)));
}
public static void main(
String args[])
throws Exception
{
//
// set up our certs
//
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
//
// cert that issued the signing certificate
//
String signDN = "O=Bouncy Castle, C=AU";
KeyPair signKP = kpg.generateKeyPair();
X509Certificate signCert = makeCertificate(
signKP, signDN, signKP, signDN);
//
// cert we sign against
//
String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
KeyPair origKP = kpg.generateKeyPair();
X509Certificate origCert = makeCertificate(
origKP, origDN, signKP, signDN);
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
//
// create a CertStore containing the certificates we want carried
// in the signature
//
Store certs = new JcaCertStore(certList);
//
// create some smime capabilities in case someone wants to respond
//
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
//
// add an encryption key preference for encrypted responses -
// normally this would be different from the signing certificate...
//
IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber(
new X500Name(signDN), origCert.getSerialNumber());
signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer));
//
// create the generator for creating an smime/signed message
//
SMIMESignedGenerator gen = new SMIMESignedGenerator();
//
// add a signer to the generator - this specifies we are using SHA1 and
// adding the smime attributes above to the signed attributes that
// will be generated as part of the signature. The encryption algorithm
// used is taken from the key - in this RSA with PKCS1Padding
//
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert));
//
// add our pool of certs and cerls (if any) to go with the signature
//
gen.addCertificates(certs);
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[0]))));
msg.setHeader("Content-Type", "application/octet-stream");
msg.setHeader("Content-Transfer-Encoding", "base64");
//
// extract the multipart object from the SMIMESigned object.
//
MimeMultipart mm = gen.generate(msg);
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(mm, mm.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("signed.message"));
}
}

View File

@@ -0,0 +1,220 @@
package org.bouncycastle.mail.smime.examples;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.Store;
/**
* a simple example that creates a single signed mail message.
*/
public class CreateSignedMail
{
//
// certificate serial number seed.
//
static int serialNo = 1;
static AuthorityKeyIdentifier createAuthorityKeyId(
PublicKey pub)
throws IOException
{
ByteArrayInputStream bIn = new ByteArrayInputStream(pub.getEncoded());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
(ASN1Sequence)new ASN1InputStream(bIn).readObject());
return new AuthorityKeyIdentifier(info);
}
static SubjectKeyIdentifier createSubjectKeyId(
PublicKey pub)
throws IOException
{
ByteArrayInputStream bIn = new ByteArrayInputStream(pub.getEncoded());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
(ASN1Sequence)new ASN1InputStream(bIn).readObject());
return new SubjectKeyIdentifier(info);
}
/**
* create a basic X509 certificate from the given keys
*/
static X509Certificate makeCertificate(
KeyPair subKP,
String subDN,
KeyPair issKP,
String issDN)
throws GeneralSecurityException, IOException, OperatorCreationException
{
PublicKey subPub = subKP.getPublic();
PrivateKey issPriv = issKP.getPrivate();
PublicKey issPub = issKP.getPublic();
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN), BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);
v3CertGen.addExtension(
X509Extension.subjectKeyIdentifier,
false,
createSubjectKeyId(subPub));
v3CertGen.addExtension(
X509Extension.authorityKeyIdentifier,
false,
createAuthorityKeyId(issPub));
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv)));
}
public static void main(
String args[])
throws Exception
{
//
// set up our certs
//
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
//
// cert that issued the signing certificate
//
String signDN = "O=Bouncy Castle, C=AU";
KeyPair signKP = kpg.generateKeyPair();
X509Certificate signCert = makeCertificate(
signKP, signDN, signKP, signDN);
//
// cert we sign against
//
String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
KeyPair origKP = kpg.generateKeyPair();
X509Certificate origCert = makeCertificate(
origKP, origDN, signKP, signDN);
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
//
// create a CertStore containing the certificates we want carried
// in the signature
//
Store certs = new JcaCertStore(certList);
//
// create some smime capabilities in case someone wants to respond
//
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
//
// add an encryption key preference for encrypted responses -
// normally this would be different from the signing certificate...
//
IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber(
new X500Name(signDN), origCert.getSerialNumber());
signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer));
//
// create the generator for creating an smime/signed message
//
SMIMESignedGenerator gen = new SMIMESignedGenerator();
//
// add a signer to the generator - this specifies we are using SHA1 and
// adding the smime attributes above to the signed attributes that
// will be generated as part of the signature. The encryption algorithm
// used is taken from the key - in this RSA with PKCS1Padding
//
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert));
//
// add our pool of certs and cerls (if any) to go with the signature
//
gen.addCertificates(certs);
//
// create the base for our message
//
MimeBodyPart msg = new MimeBodyPart();
msg.setText("Hello world!");
//
// extract the multipart object from the SMIMESigned object.
//
MimeMultipart mm = gen.generate(msg);
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(mm, mm.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("signed.message"));
}
}

View File

@@ -0,0 +1,213 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.Store;
/**
* a simple example that creates a single signed multipart mail message.
*/
public class CreateSignedMultipartMail
{
//
// certificate serial number seed.
//
static int serialNo = 1;
/**
* create a basic X509 certificate from the given keys
*/
static X509Certificate makeCertificate(
KeyPair subKP,
String subDN,
KeyPair issKP,
String issDN)
throws GeneralSecurityException, IOException, OperatorCreationException
{
PublicKey subPub = subKP.getPublic();
PrivateKey issPriv = issKP.getPrivate();
PublicKey issPub = issKP.getPublic();
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN), BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);
v3CertGen.addExtension(
X509Extension.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(subPub));
v3CertGen.addExtension(
X509Extension.authorityKeyIdentifier,
false,
extUtils.createAuthorityKeyIdentifier(issPub));
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv)));
}
public static void main(
String args[])
throws Exception
{
//
// set up our certs
//
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
//
// cert that issued the signing certificate
//
String signDN = "O=Bouncy Castle, C=AU";
KeyPair signKP = kpg.generateKeyPair();
X509Certificate signCert = makeCertificate(
signKP, signDN, signKP, signDN);
//
// cert we sign against
//
String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
KeyPair origKP = kpg.generateKeyPair();
X509Certificate origCert = makeCertificate(
origKP, origDN, signKP, signDN);
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
//
// create a CertStore containing the certificates we want carried
// in the signature
//
Store certs = new JcaCertStore(certList);
//
// create some smime capabilities in case someone wants to respond
//
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
//
// add an encryption key preference for encrypted responses -
// normally this would be different from the signing certificate...
//
IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber(
new X500Name(signDN), origCert.getSerialNumber());
signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer));
//
// create the generator for creating an smime/signed message
//
SMIMESignedGenerator gen = new SMIMESignedGenerator();
//
// add a signer to the generator - this specifies we are using SHA1 and
// adding the smime attributes above to the signed attributes that
// will be generated as part of the signature. The encryption algorithm
// used is taken from the key - in this RSA with PKCS1Padding
//
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert));
//
// add our pool of certs and cerls (if any) to go with the signature
//
gen.addCertificates(certs);
//
// create the base for our message
//
MimeBodyPart msg1 = new MimeBodyPart();
msg1.setText("Hello part 1!");
MimeBodyPart msg2 = new MimeBodyPart();
msg2.setText("Hello part 2!");
MimeMultipart mp = new MimeMultipart();
mp.addBodyPart(msg1);
mp.addBodyPart(msg2);
MimeBodyPart m = new MimeBodyPart();
//
// be careful about setting extra headers here. Some mail clients
// ignore the To and From fields (for example) in the body part
// that contains the multipart. The result of this will be that the
// signature fails to verify... Outlook Express is an example of
// a client that exhibits this behaviour.
//
m.setContent(mp);
//
// extract the multipart object from the SMIMESigned object.
//
MimeMultipart mm = gen.generate(m);
//
// Get a Session object and create the mail message
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(mm, mm.getContentType());
body.saveChanges();
body.writeTo(new FileOutputStream("signed.message"));
}
}

View File

@@ -0,0 +1,77 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.util.Enumeration;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
public class ExampleUtils
{
/**
* Dump the content of the passed in BodyPart to the file fileName.
*
* @throws MessagingException
* @throws IOException
*/
public static void dumpContent(
MimeBodyPart bodyPart,
String fileName)
throws MessagingException, IOException
{
//
// print mime type of compressed content
//
System.out.println("content type: " + bodyPart.getContentType());
//
// recover the compressed content
//
OutputStream out = new FileOutputStream(fileName);
InputStream in = bodyPart.getInputStream();
byte[] buf = new byte[10000];
int len;
while ((len = in.read(buf, 0, buf.length)) > 0)
{
out.write(buf, 0, len);
}
out.close();
}
public static String findKeyAlias(
KeyStore store,
String storeName,
char[] password)
throws Exception
{
store.load(new FileInputStream(storeName), password);
Enumeration e = store.aliases();
String keyAlias = null;
while (e.hasMoreElements())
{
String alias = (String)e.nextElement();
if (store.isKeyEntry(alias))
{
keyAlias = alias;
}
}
if (keyAlias == null)
{
throw new IllegalArgumentException("can't find a private key in keyStore: " + storeName);
}
return keyAlias;
}
}

View File

@@ -0,0 +1,41 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.jcajce.ZlibExpanderProvider;
import org.bouncycastle.mail.smime.SMIMECompressed;
import org.bouncycastle.mail.smime.SMIMEUtil;
/**
* a simple example that reads a compressed email.
* <p>
*/
public class ReadCompressedMail
{
public static void main(
String args[])
throws Exception
{
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new FileInputStream("compressed.message"));
SMIMECompressed m = new SMIMECompressed(msg);
MimeBodyPart res = SMIMEUtil.toMimeBodyPart(m.getContent(new ZlibExpanderProvider()));
System.out.println("Message Contents");
System.out.println("----------------");
System.out.println(res.getContent());
}
}

View File

@@ -0,0 +1,94 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEUtil;
/**
* a simple example that reads an encrypted email.
* <p>
* The key store can be created using the class in
* org.bouncycastle.jce.examples.PKCS12Example - the program expects only one
* key to be present.
*/
public class ReadEncryptedMail
{
public static void main(
String args[])
throws Exception
{
if (args.length != 2)
{
System.err.println("usage: ReadEncryptedMail pkcs12Keystore password");
System.exit(0);
}
//
// Open the key store
//
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
ks.load(new FileInputStream(args[0]), args[1].toCharArray());
Enumeration e = ks.aliases();
String keyAlias = null;
while (e.hasMoreElements())
{
String alias = (String)e.nextElement();
if (ks.isKeyEntry(alias))
{
keyAlias = alias;
}
}
if (keyAlias == null)
{
System.err.println("can't find a private key!");
System.exit(0);
}
//
// find the certificate for the private key and generate a
// suitable recipient identifier.
//
X509Certificate cert = (X509Certificate)ks.getCertificate(keyAlias);
RecipientId recId = new JceKeyTransRecipientId(cert);
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new FileInputStream("encrypted.message"));
SMIMEEnveloped m = new SMIMEEnveloped(msg);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipient = recipients.get(recId);
MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient((PrivateKey)ks.getKey(keyAlias, null)).setProvider("BC")));
System.out.println("Message Contents");
System.out.println("----------------");
System.out.println(res.getContent());
}
}

View File

@@ -0,0 +1,38 @@
package org.bouncycastle.mail.smime.examples;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.jcajce.ZlibExpanderProvider;
import org.bouncycastle.mail.smime.SMIMECompressedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.mail.smime.util.SharedFileInputStream;
/**
* a simple example that reads an oversize compressed email and writes data contained
* in the compressed part into a file.
*/
public class ReadLargeCompressedMail
{
public static void main(
String args[])
throws Exception
{
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("compressed.message"));
SMIMECompressedParser m = new SMIMECompressedParser(msg);
MimeBodyPart res = SMIMEUtil.toMimeBodyPart(m.getContent(new ZlibExpanderProvider()));
ExampleUtils.dumpContent(res, args[0]);
}
}

View File

@@ -0,0 +1,71 @@
package org.bouncycastle.mail.smime.examples;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.mail.smime.SMIMEEnvelopedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.mail.smime.util.SharedFileInputStream;
/**
* a simple example that reads an encrypted email using the large file model.
* <p>
* The key store can be created using the class in
* org.bouncycastle.jce.examples.PKCS12Example - the program expects only one
* key to be present.
*/
public class ReadLargeEncryptedMail
{
public static void main(
String args[])
throws Exception
{
if (args.length != 3)
{
System.err.println("usage: ReadLargeEncryptedMail pkcs12Keystore password outputFile");
System.exit(0);
}
//
// Open the key store
//
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
String keyAlias = ExampleUtils.findKeyAlias(ks, args[0], args[1].toCharArray());
//
// find the certificate for the private key and generate a
// suitable recipient identifier.
//
X509Certificate cert = (X509Certificate)ks.getCertificate(keyAlias);
RecipientId recId = new JceKeyTransRecipientId(cert);
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("encrypted.message"));
SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(msg);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipient = recipients.get(recId);
MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransEnvelopedRecipient((PrivateKey)ks.getKey(keyAlias, null)).setProvider("BC")));
ExampleUtils.dumpContent(res, args[2]);
}
}

View File

@@ -0,0 +1,125 @@
package org.bouncycastle.mail.smime.examples;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMESignedParser;
import org.bouncycastle.mail.smime.util.SharedFileInputStream;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
/**
* a simple example that reads a basic SMIME signed mail file.
*/
public class ReadLargeSignedMail
{
private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
/**
* verify the signature (assuming the cert is contained in the message)
*/
private static void verify(
SMIMESignedParser s)
throws Exception
{
//
// extract the information to verify the signatures.
//
//
// certificates and crls passed in the signature - this must happen before
// s.getSignerInfos()
//
Store certs = s.getCertificates();
//
// SignerInfo blocks which contain the signatures
//
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
//
// check each signer
//
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate((X509CertificateHolder)certIt.next());
//
// verify that the sig is correct and that it was generated
// when the certificate was current
//
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert)))
{
System.out.println("signature verified");
}
else
{
System.out.println("signature failed!");
}
}
}
public static void main(
String[] args)
throws Exception
{
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("signed.message"));
//
// make sure this was a multipart/signed message - there should be
// two parts as we have one part for the content that was signed and
// one part for the actual signature.
//
if (msg.isMimeType("multipart/signed"))
{
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(),
(MimeMultipart)msg.getContent());
System.out.println("Status:");
verify(s);
}
else if (msg.isMimeType("application/pkcs7-mime"))
{
//
// in this case the content is wrapped in the signature block.
//
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(), msg);
System.out.println("Status:");
verify(s);
}
else
{
System.err.println("Not a signed message!");
}
}
}

View File

@@ -0,0 +1,176 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.util.Store;
/**
* a simple example that reads a basic SMIME signed mail file.
*/
public class ReadSignedMail
{
private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
/**
* verify the signature (assuming the cert is contained in the message)
*/
private static void verify(
SMIMESigned s)
throws Exception
{
//
// extract the information to verify the signatures.
//
//
// certificates and crls passed in the signature
//
Store certs = s.getCertificates();
//
// SignerInfo blocks which contain the signatures
//
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
//
// check each signer
//
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate((X509CertificateHolder)certIt.next());
//
// verify that the sig is correct and that it was generated
// when the certificate was current
//
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert)))
{
System.out.println("signature verified");
}
else
{
System.out.println("signature failed!");
}
}
}
public static void main(
String[] args)
throws Exception
{
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage msg = new MimeMessage(session, new FileInputStream("signed.message"));
//
// make sure this was a multipart/signed message - there should be
// two parts as we have one part for the content that was signed and
// one part for the actual signature.
//
if (msg.isMimeType("multipart/signed"))
{
SMIMESigned s = new SMIMESigned(
(MimeMultipart)msg.getContent());
//
// extract the content
//
MimeBodyPart content = s.getContent();
System.out.println("Content:");
Object cont = content.getContent();
if (cont instanceof String)
{
System.out.println((String)cont);
}
else if (cont instanceof Multipart)
{
Multipart mp = (Multipart)cont;
int count = mp.getCount();
for (int i = 0; i < count; i++)
{
BodyPart m = mp.getBodyPart(i);
Object part = m.getContent();
System.out.println("Part " + i);
System.out.println("---------------------------");
if (part instanceof String)
{
System.out.println((String)part);
}
else
{
System.out.println("can't print...");
}
}
}
System.out.println("Status:");
verify(s);
}
else if (msg.isMimeType("application/pkcs7-mime")
|| msg.isMimeType("application/x-pkcs7-mime"))
{
//
// in this case the content is wrapped in the signature block.
//
SMIMESigned s = new SMIMESigned(msg);
//
// extract the content
//
MimeBodyPart content = s.getContent();
System.out.println("Content:");
Object cont = content.getContent();
if (cont instanceof String)
{
System.out.println((String)cont);
}
System.out.println("Status:");
verify(s);
}
else
{
System.err.println("Not a signed message!");
}
}
}

View File

@@ -0,0 +1,192 @@
package org.bouncycastle.mail.smime.examples;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.Strings;
/**
* Example that sends a signed and encrypted mail message.
*/
public class SendSignedAndEncryptedMail
{
public static void main(String args[])
{
if (args.length != 5)
{
System.err
.println("usage: SendSignedAndEncryptedMail <pkcs12Keystore> <password> <keyalias> <smtp server> <email address>");
System.exit(0);
}
try
{
MailcapCommandMap mailcap = (MailcapCommandMap)CommandMap
.getDefaultCommandMap();
mailcap
.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
mailcap
.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
mailcap
.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
mailcap
.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
mailcap
.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
CommandMap.setDefaultCommandMap(mailcap);
/* Add BC */
Security.addProvider(new BouncyCastleProvider());
/* Open the keystore */
KeyStore keystore = KeyStore.getInstance("PKCS12", "BC");
keystore.load(new FileInputStream(args[0]), args[1].toCharArray());
Certificate[] chain = keystore.getCertificateChain(args[2]);
/* Get the private key to sign the message with */
PrivateKey privateKey = (PrivateKey)keystore.getKey(args[2],
args[1].toCharArray());
if (privateKey == null)
{
throw new Exception("cannot find private key for alias: "
+ args[2]);
}
/* Create the message to sign and encrypt */
Properties props = System.getProperties();
props.put("mail.smtp.host", args[3]);
Session session = Session.getDefaultInstance(props, null);
MimeMessage body = new MimeMessage(session);
body.setFrom(new InternetAddress(args[4]));
body.setRecipient(Message.RecipientType.TO, new InternetAddress(
args[4]));
body.setSubject("example encrypted message");
body.setContent("example encrypted message", "text/plain");
body.saveChanges();
/* Create the SMIMESignedGenerator */
SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
capabilities.addCapability(SMIMECapability.dES_CBC);
ASN1EncodableVector attributes = new ASN1EncodableVector();
attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(
new IssuerAndSerialNumber(
new X500Name(((X509Certificate)chain[0])
.getIssuerDN().getName()),
((X509Certificate)chain[0]).getSerialNumber())));
attributes.add(new SMIMECapabilitiesAttribute(capabilities));
SMIMESignedGenerator signer = new SMIMESignedGenerator();
signer.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(attributes)).build("DSA".equals(privateKey.getAlgorithm()) ? "SHA1withDSA" : "MD5withRSA", privateKey, (X509Certificate)chain[0]));
/* Add the list of certs to the generator */
List certList = new ArrayList();
certList.add(chain[0]);
Store certs = new JcaCertStore(certList);
signer.addCertificates(certs);
/* Sign the message */
MimeMultipart mm = signer.generate(body);
MimeMessage signedMessage = new MimeMessage(session);
/* Set all original MIME headers in the signed message */
Enumeration headers = body.getAllHeaderLines();
while (headers.hasMoreElements())
{
signedMessage.addHeaderLine((String)headers.nextElement());
}
/* Set the content of the signed message */
signedMessage.setContent(mm);
signedMessage.saveChanges();
/* Create the encrypter */
SMIMEEnvelopedGenerator encrypter = new SMIMEEnvelopedGenerator();
encrypter.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC"));
/* Encrypt the message */
MimeBodyPart encryptedPart = encrypter.generate(signedMessage,
new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build());
/*
* Create a new MimeMessage that contains the encrypted and signed
* content
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();
encryptedPart.writeTo(out);
MimeMessage encryptedMessage = new MimeMessage(session,
new ByteArrayInputStream(out.toByteArray()));
/* Set all original MIME headers in the encrypted message */
headers = body.getAllHeaderLines();
while (headers.hasMoreElements())
{
String headerLine = (String)headers.nextElement();
/*
* Make sure not to override any content-* headers from the
* original message
*/
if (!Strings.toLowerCase(headerLine).startsWith("content-"))
{
encryptedMessage.addHeaderLine(headerLine);
}
}
Transport.send(encryptedMessage);
}
catch (SMIMEException ex)
{
ex.getUnderlyingException().printStackTrace(System.err);
ex.printStackTrace(System.err);
}
catch (Exception ex)
{
ex.printStackTrace(System.err);
}
}
}

View File

@@ -0,0 +1,352 @@
package org.bouncycastle.mail.smime.examples;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.i18n.ErrorBundle;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.validator.SignedMailValidator;
import org.bouncycastle.x509.PKIXCertPathReviewer;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
/**
* An Example that reads a signed mail and validates its signature. Also
* validating the certificate path from the signers key to a trusted entity
*/
public class ValidateSignedMail
{
/*
* Use trusted certificates from $JAVA_HOME/lib/security/cacerts as
* trustanchors
*/
public static final boolean useCaCerts = false;
public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
//
// Get a Session object with the default properties.
//
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// read message
MimeMessage msg = new MimeMessage(session, new FileInputStream(
"signed.message"));
// create PKIXparameters
PKIXParameters param;
if (useCaCerts)
{
KeyStore caCerts = KeyStore.getInstance("JKS");
String javaHome = System.getProperty("java.home");
caCerts.load(
new FileInputStream(javaHome + "/lib/security/cacerts"),
"changeit".toCharArray());
param = new PKIXParameters(caCerts);
}
else
{
// load trustanchors from files (here we only load one)
Set trustanchors = new HashSet();
TrustAnchor trust = getTrustAnchor("trustanchor");
// create a dummy trustanchor if we can not find any trustanchor. so
// we can still try to validate the message
if (trust == null)
{
System.out
.println("no trustanchor file found, using a dummy trustanchor");
trust = getDummyTrustAnchor();
}
trustanchors.add(trust);
param = new PKIXParameters(trustanchors);
}
// load one ore more crls from files (here we only load one crl)
List crls = new ArrayList();
X509CRL crl = loadCRL("crl.file");
if (crl != null)
{
crls.add(crl);
}
CertStore certStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(crls), "BC");
// add crls and enable revocation checking
param.addCertStore(certStore);
param.setRevocationEnabled(true);
// or disable revocation checking
// param.setRevocationEnabled(false);
verifySignedMail(msg, param);
}
public static final int TITLE = 0;
public static final int TEXT = 1;
public static final int SUMMARY = 2;
public static final int DETAIL = 3;
static int dbgLvl = DETAIL;
private static final String RESOURCE_NAME = "org.bouncycastle.mail.smime.validator.SignedMailValidatorMessages";
public static void verifySignedMail(MimeMessage msg, PKIXParameters param)
throws Exception
{
// set locale for the output
Locale loc = Locale.ENGLISH;
// Locale loc = Locale.GERMAN;
// validate signatures
SignedMailValidator validator = new SignedMailValidator(msg, param);
// iterate over all signatures and print results
Iterator it = validator.getSignerInformationStore().getSigners()
.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation) it.next();
SignedMailValidator.ValidationResult result = validator
.getValidationResult(signer);
if (result.isValidSignature())
{
ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.sigValid");
System.out.println(errMsg.getText(loc));
}
else
{
ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.sigInvalid");
System.out.println(errMsg.getText(loc));
// print errors
System.out.println("Errors:");
Iterator errorsIt = result.getErrors().iterator();
while (errorsIt.hasNext())
{
ErrorBundle errorMsg = (ErrorBundle) errorsIt.next();
if (dbgLvl == DETAIL)
{
System.out.println("\t\t" + errorMsg.getDetail(loc));
}
else
{
System.out.println("\t\t" + errorMsg.getText(loc));
}
}
}
if (!result.getNotifications().isEmpty())
{
System.out.println("Notifications:");
Iterator notIt = result.getNotifications().iterator();
while (notIt.hasNext())
{
ErrorBundle notMsg = (ErrorBundle) notIt.next();
if (dbgLvl == DETAIL)
{
System.out.println("\t\t" + notMsg.getDetail(loc));
}
else
{
System.out.println("\t\t" + notMsg.getText(loc));
}
}
}
PKIXCertPathReviewer review = result.getCertPathReview();
if (review != null)
{
if (review.isValidCertPath())
{
System.out.println("Certificate path valid");
}
else
{
System.out.println("Certificate path invalid");
}
System.out.println("\nCertificate path validation results:");
// global errors
System.out.println("Errors:");
Iterator errorsIt = review.getErrors(-1).iterator();
while (errorsIt.hasNext())
{
ErrorBundle errorMsg = (ErrorBundle) errorsIt.next();
if (dbgLvl == DETAIL)
{
System.out.println("\t\t" + errorMsg.getDetail(loc));
}
else
{
System.out.println("\t\t" + errorMsg.getText(loc));
}
}
System.out.println("Notifications:");
Iterator notificationsIt = review.getNotifications(-1)
.iterator();
while (notificationsIt.hasNext())
{
ErrorBundle noteMsg = (ErrorBundle) notificationsIt.next();
System.out.println("\t" + noteMsg.getText(loc));
}
// per certificate errors and notifications
Iterator certIt = review.getCertPath().getCertificates()
.iterator();
int i = 0;
while (certIt.hasNext())
{
X509Certificate cert = (X509Certificate) certIt.next();
System.out.println("\nCertificate " + i + "\n========");
System.out.println("Issuer: "
+ cert.getIssuerDN().getName());
System.out.println("Subject: "
+ cert.getSubjectDN().getName());
// errors
System.out.println("\tErrors:");
errorsIt = review.getErrors(i).iterator();
while (errorsIt.hasNext())
{
ErrorBundle errorMsg = (ErrorBundle) errorsIt.next();
if (dbgLvl == DETAIL)
{
System.out
.println("\t\t" + errorMsg.getDetail(loc));
}
else
{
System.out.println("\t\t" + errorMsg.getText(loc));
}
}
// notifications
System.out.println("\tNotifications:");
notificationsIt = review.getNotifications(i).iterator();
while (notificationsIt.hasNext())
{
ErrorBundle noteMsg = (ErrorBundle) notificationsIt
.next();
if (dbgLvl == DETAIL)
{
System.out.println("\t\t" + noteMsg.getDetail(loc));
}
else
{
System.out.println("\t\t" + noteMsg.getText(loc));
}
}
i++;
}
}
}
}
protected static TrustAnchor getTrustAnchor(String trustcert)
throws Exception
{
X509Certificate cert = loadCert(trustcert);
if (cert != null)
{
byte[] ncBytes = cert
.getExtensionValue(X509Extension.nameConstraints.getId());
if (ncBytes != null)
{
ASN1Encodable extValue = X509ExtensionUtil
.fromExtensionValue(ncBytes);
return new TrustAnchor(cert, extValue.toASN1Primitive().getEncoded(ASN1Encoding.DER));
}
return new TrustAnchor(cert, null);
}
return null;
}
protected static X509Certificate loadCert(String certfile)
{
X509Certificate cert = null;
try
{
InputStream in = new FileInputStream(certfile);
CertificateFactory cf = CertificateFactory.getInstance("X.509",
"BC");
cert = (X509Certificate) cf.generateCertificate(in);
}
catch (Exception e)
{
System.out.println("certfile \"" + certfile
+ "\" not found - classpath is "
+ System.getProperty("java.class.path"));
}
return cert;
}
protected static X509CRL loadCRL(String crlfile)
{
X509CRL crl = null;
try
{
InputStream in = new FileInputStream(crlfile);
CertificateFactory cf = CertificateFactory.getInstance("X.509",
"BC");
crl = (X509CRL) cf.generateCRL(in);
}
catch (Exception e)
{
System.out.println("crlfile \"" + crlfile
+ "\" not found - classpath is "
+ System.getProperty("java.class.path"));
}
return crl;
}
private static TrustAnchor getDummyTrustAnchor() throws Exception
{
X500Principal principal = new X500Principal("CN=Dummy Trust Anchor");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
PublicKey trustPubKey = kpg.generateKeyPair().getPublic();
return new TrustAnchor(principal, trustPubKey, null);
}
}

View File

@@ -0,0 +1,110 @@
package org.bouncycastle.mail.smime.handlers;
import java.awt.datatransfer.DataFlavor;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.activation.ActivationDataFlavor;
import javax.activation.DataContentHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import org.bouncycastle.mail.smime.SMIMEStreamingProcessor;
public class PKCS7ContentHandler
implements DataContentHandler
{
private final ActivationDataFlavor _adf;
private final DataFlavor[] _dfs;
PKCS7ContentHandler(
ActivationDataFlavor adf,
DataFlavor[] dfs)
{
_adf = adf;
_dfs = dfs;
}
public Object getContent(
DataSource ds)
throws IOException
{
return ds.getInputStream();
}
public Object getTransferData(
DataFlavor df,
DataSource ds)
throws IOException
{
if (_adf.equals(df))
{
return getContent(ds);
}
else
{
return null;
}
}
public DataFlavor[] getTransferDataFlavors()
{
return _dfs;
}
public void writeTo(
Object obj,
String mimeType,
OutputStream os)
throws IOException
{
if (obj instanceof MimeBodyPart)
{
try
{
((MimeBodyPart)obj).writeTo(os);
}
catch (MessagingException ex)
{
throw new IOException(ex.getMessage());
}
}
else if (obj instanceof byte[])
{
os.write((byte[])obj);
}
else if (obj instanceof InputStream)
{
int b;
InputStream in = (InputStream)obj;
if (!(in instanceof BufferedInputStream))
{
in = new BufferedInputStream(in);
}
while ((b = in.read()) >= 0)
{
os.write(b);
}
}
else if (obj instanceof SMIMEStreamingProcessor)
{
SMIMEStreamingProcessor processor = (SMIMEStreamingProcessor)obj;
processor.write(os);
}
else
{
// TODO it would be even nicer if we could attach the object to the exception
// as well since in deeply nested messages, it is not always clear which
// part caused the problem. Thus I guess we would have to subclass the
// IOException
throw new IOException("unknown object in writeTo " + obj);
}
}
}

View File

@@ -0,0 +1,280 @@
package org.bouncycastle.mail.smime.handlers;
import org.bouncycastle.mail.smime.SMIMEStreamingProcessor;
import javax.activation.ActivationDataFlavor;
import javax.activation.DataContentHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import java.awt.datatransfer.DataFlavor;
import java.io.BufferedInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
public class multipart_signed
implements DataContentHandler
{
private static final ActivationDataFlavor ADF = new ActivationDataFlavor(MimeMultipart.class, "multipart/signed", "Multipart Signed");
private static final DataFlavor[] DFS = new DataFlavor[] { ADF };
public Object getContent(DataSource ds)
throws IOException
{
try
{
return new MimeMultipart(ds);
}
catch (MessagingException ex)
{
return null;
}
}
public Object getTransferData(DataFlavor df, DataSource ds)
throws IOException
{
if (ADF.equals(df))
{
return getContent(ds);
}
else
{
return null;
}
}
public DataFlavor[] getTransferDataFlavors()
{
return DFS;
}
public void writeTo(Object obj, String _mimeType, OutputStream os)
throws IOException
{
if (obj instanceof MimeMultipart)
{
try
{
outputBodyPart(os, obj);
}
catch (MessagingException ex)
{
throw new IOException(ex.getMessage());
}
}
else if(obj instanceof byte[])
{
os.write((byte[])obj);
}
else if (obj instanceof InputStream)
{
int b;
InputStream in = (InputStream)obj;
if (!(in instanceof BufferedInputStream))
{
in = new BufferedInputStream(in);
}
while ((b = in.read()) >= 0)
{
os.write(b);
}
}
else if (obj instanceof SMIMEStreamingProcessor)
{
SMIMEStreamingProcessor processor = (SMIMEStreamingProcessor)obj;
processor.write(os);
}
else
{
throw new IOException("unknown object in writeTo " + obj);
}
}
/*
* Output the mulitpart as a collection of leaves to make sure preamble text is not included.
*/
private void outputBodyPart(
OutputStream out,
Object bodyPart)
throws MessagingException, IOException
{
if (bodyPart instanceof Multipart)
{
Multipart mp = (Multipart)bodyPart;
ContentType contentType = new ContentType(mp.getContentType());
String boundary = "--" + contentType.getParameter("boundary");
LineOutputStream lOut = new LineOutputStream(out);
for (int i = 0; i < mp.getCount(); i++)
{
lOut.writeln(boundary);
outputBodyPart(out, mp.getBodyPart(i));
lOut.writeln(); // CRLF terminator
}
lOut.writeln(boundary + "--");
return;
}
MimeBodyPart mimePart = (MimeBodyPart)bodyPart;
if (mimePart.getContent() instanceof Multipart)
{
Multipart mp = (Multipart)mimePart.getContent();
ContentType contentType = new ContentType(mp.getContentType());
String boundary = "--" + contentType.getParameter("boundary");
LineOutputStream lOut = new LineOutputStream(out);
Enumeration headers = mimePart.getAllHeaderLines();
while (headers.hasMoreElements())
{
lOut.writeln((String)headers.nextElement());
}
lOut.writeln(); // CRLF separator
outputPreamble(lOut, mimePart, boundary);
outputBodyPart(out, mp);
return;
}
mimePart.writeTo(out);
}
/**
* internal preamble is generally included in signatures, while this is technically wrong,
* if we find internal preamble we include it by default.
*/
static void outputPreamble(LineOutputStream lOut, MimeBodyPart part, String boundary)
throws MessagingException, IOException
{
InputStream in;
try
{
in = part.getRawInputStream();
}
catch (MessagingException e)
{
return; // no underlying content, rely on default generation
}
String line;
while ((line = readLine(in)) != null)
{
if (line.equals(boundary))
{
break;
}
lOut.writeln(line);
}
in.close();
if (line == null)
{
throw new MessagingException("no boundary found");
}
}
/*
* read a line of input stripping of the tailing \r\n
*/
private static String readLine(InputStream in)
throws IOException
{
StringBuffer b = new StringBuffer();
int ch;
while ((ch = in.read()) >= 0 && ch != '\n')
{
if (ch != '\r')
{
b.append((char)ch);
}
}
if (ch < 0)
{
return null;
}
return b.toString();
}
private static class LineOutputStream extends FilterOutputStream
{
private static byte newline[];
public LineOutputStream(OutputStream outputstream)
{
super(outputstream);
}
public void writeln(String s)
throws MessagingException
{
try
{
byte abyte0[] = getBytes(s);
super.out.write(abyte0);
super.out.write(newline);
}
catch(Exception exception)
{
throw new MessagingException("IOException", exception);
}
}
public void writeln()
throws MessagingException
{
try
{
super.out.write(newline);
}
catch(Exception exception)
{
throw new MessagingException("IOException", exception);
}
}
static
{
newline = new byte[2];
newline[0] = 13;
newline[1] = 10;
}
private static byte[] getBytes(String s)
{
char ac[] = s.toCharArray();
int i = ac.length;
byte abyte0[] = new byte[i];
int j = 0;
while (j < i)
{
abyte0[j] = (byte)ac[j++];
}
return abyte0;
}
}
}

View File

@@ -0,0 +1,18 @@
package org.bouncycastle.mail.smime.handlers;
import java.awt.datatransfer.DataFlavor;
import javax.activation.ActivationDataFlavor;
import javax.mail.internet.MimeBodyPart;
public class pkcs7_mime
extends PKCS7ContentHandler
{
private static final ActivationDataFlavor ADF = new ActivationDataFlavor(MimeBodyPart.class, "application/pkcs7-mime", "Encrypted Data");
private static final DataFlavor[] DFS = new DataFlavor[] { ADF };
public pkcs7_mime()
{
super(ADF, DFS);
}
}

View File

@@ -0,0 +1,18 @@
package org.bouncycastle.mail.smime.handlers;
import java.awt.datatransfer.DataFlavor;
import javax.activation.ActivationDataFlavor;
import javax.mail.internet.MimeBodyPart;
public class pkcs7_signature
extends PKCS7ContentHandler
{
private static final ActivationDataFlavor ADF = new ActivationDataFlavor(MimeBodyPart.class, "application/pkcs7-signature", "Signature");
private static final DataFlavor[] DFS = new DataFlavor[] { ADF };
public pkcs7_signature()
{
super(ADF, DFS);
}
}

View File

@@ -0,0 +1,18 @@
package org.bouncycastle.mail.smime.handlers;
import java.awt.datatransfer.DataFlavor;
import javax.activation.ActivationDataFlavor;
import javax.mail.internet.MimeBodyPart;
public class x_pkcs7_mime
extends PKCS7ContentHandler
{
private static final ActivationDataFlavor ADF = new ActivationDataFlavor(MimeBodyPart.class, "application/x-pkcs7-mime", "Encrypted Data");
private static final DataFlavor[] DFS = new DataFlavor[] { ADF };
public x_pkcs7_mime()
{
super(ADF, DFS);
}
}

View File

@@ -0,0 +1,90 @@
package org.bouncycastle.mail.smime.handlers;
import java.awt.datatransfer.DataFlavor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.activation.ActivationDataFlavor;
import javax.activation.DataContentHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
public class x_pkcs7_signature
implements DataContentHandler
{
/*
*
* VARIABLES
*
*/
private static final ActivationDataFlavor ADF;
private static final DataFlavor[] ADFs;
static
{
ADF = new ActivationDataFlavor(MimeBodyPart.class, "application/x-pkcs7-signature", "Signature");
ADFs = new DataFlavor[] { ADF };
}
public Object getContent(DataSource _ds)
throws IOException
{
return _ds.getInputStream();
}
public Object getTransferData(DataFlavor _df, DataSource _ds)
throws IOException
{
if (ADF.equals(_df))
{
return getContent(_ds);
}
else
{
return null;
}
}
public DataFlavor[] getTransferDataFlavors()
{
return ADFs;
}
public void writeTo(Object _obj, String _mimeType, OutputStream _os)
throws IOException
{
if (_obj instanceof MimeBodyPart)
{
try
{
((MimeBodyPart)_obj).writeTo(_os);
}
catch (MessagingException ex)
{
throw new IOException(ex.getMessage());
}
}
else if (_obj instanceof byte[])
{
_os.write((byte[])_obj);
}
else if (_obj instanceof InputStream)
{
int b;
InputStream in = (InputStream)_obj;
while ((b = in.read()) >= 0)
{
_os.write(b);
}
}
else
{
throw new IOException("unknown object in writeTo " + _obj);
}
}
}

View File

@@ -0,0 +1,67 @@
package org.bouncycastle.mail.smime.util;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class CRLFOutputStream extends FilterOutputStream
{
protected int lastb;
protected static byte newline[];
public CRLFOutputStream(OutputStream outputstream)
{
super(outputstream);
lastb = -1;
}
public void write(int i)
throws IOException
{
if (i == '\r')
{
out.write(newline);
}
else if (i == '\n')
{
if (lastb != '\r')
{
out.write(newline);
}
}
else
{
out.write(i);
}
lastb = i;
}
public void write(byte[] buf)
throws IOException
{
this.write(buf, 0, buf.length);
}
public void write(byte buf[], int off, int len)
throws IOException
{
for (int i = off; i != off + len; i++)
{
this.write(buf[i]);
}
}
public void writeln()
throws IOException
{
super.out.write(newline);
}
static
{
newline = new byte[2];
newline[0] = '\r';
newline[1] = '\n';
}
}

View File

@@ -0,0 +1,162 @@
package org.bouncycastle.mail.smime.util;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeBodyPart;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
public class FileBackedMimeBodyPart
extends MimeBodyPart
{
private static final int BUF_SIZE = 32760;
private final File _file;
/**
* Create a MimeBodyPart backed by the data in file.
*
* @param file file containing the body part.
* @throws MessagingException an exception occurs parsing file.
* @throws IOException an exception occurs accessing file.
*/
public FileBackedMimeBodyPart(
File file)
throws MessagingException, IOException
{
super(new SharedFileInputStream(file));
_file = file;
}
/**
* Create a MimeBodyPart backed by file based on the headers and
* content data in content.
*
* @param content an inputstream containing the body part.
* @param file a handle to the backing file to use for storage.
* @throws MessagingException an exception occurs parsing the resulting body part in file.
* @throws IOException an exception occurs accessing file or content.
*/
public FileBackedMimeBodyPart(
InputStream content,
File file)
throws MessagingException, IOException
{
this(saveStreamToFile(content, file));
}
/**
* Create a MimeBodyPart backed by file, with the headers
* given in headers and body content taken from the stream body.
*
* @param headers headers for the body part.
* @param body internal content for the body part.
* @param file backing file to use.
*
* @throws MessagingException if the body part can't be produced.
* @throws IOException if there is an issue reading stream or writing to file.
*/
public FileBackedMimeBodyPart(
InternetHeaders headers,
InputStream body,
File file)
throws MessagingException, IOException
{
this(saveStreamToFile(headers, body, file));
}
public void writeTo(
OutputStream out)
throws IOException, MessagingException
{
if (!_file.exists())
{
throw new IOException("file " + _file.getCanonicalPath() + " no longer exists.");
}
super.writeTo(out);
}
/**
* Close off the underlying shared streams and remove the backing file.
*
* @throws IOException if streams cannot be closed or the file cannot be deleted.
*/
public void dispose()
throws IOException
{
((SharedFileInputStream)contentStream).getRoot().dispose();
if (_file.exists() && !_file.delete())
{
throw new IOException("deletion of underlying file <" + _file.getCanonicalPath() + "> failed.");
}
}
private static File saveStreamToFile(InputStream content, File tempFile)
throws IOException
{
saveContentToStream(new FileOutputStream(tempFile), content);
return tempFile;
}
private static File saveStreamToFile(InternetHeaders headers, InputStream content, File tempFile)
throws IOException
{
OutputStream out = new FileOutputStream(tempFile);
Enumeration en = headers.getAllHeaderLines();
while (en.hasMoreElements())
{
writeHeader(out, (String)en.nextElement());
}
writeSeperator(out);
saveContentToStream(out, content);
return tempFile;
}
private static void writeHeader(OutputStream out, String header)
throws IOException
{
for (int i = 0; i != header.length(); i++)
{
out.write(header.charAt(i));
}
writeSeperator(out);
}
private static void writeSeperator(OutputStream out)
throws IOException
{
out.write('\r');
out.write('\n');
}
private static void saveContentToStream(
OutputStream out,
InputStream content)
throws IOException
{
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = content.read(buf, 0, buf.length)) > 0)
{
out.write(buf, 0, len);
}
out.close();
content.close();
}
}

View File

@@ -0,0 +1,241 @@
package org.bouncycastle.mail.smime.util;
import javax.mail.internet.SharedInputStream;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class SharedFileInputStream
extends FilterInputStream
implements SharedInputStream
{
private final SharedFileInputStream _parent;
private final File _file;
private final long _start;
private final long _length;
private long _position;
private long _markedPosition;
private List _subStreams = new LinkedList();
public SharedFileInputStream(
String fileName)
throws IOException
{
this(new File(fileName));
}
public SharedFileInputStream(
File file)
throws IOException
{
this(file, 0, file.length());
}
private SharedFileInputStream(
File file,
long start,
long length)
throws IOException
{
super(new BufferedInputStream(new FileInputStream(file)));
_parent = null;
_file = file;
_start = start;
_length = length;
in.skip(start);
}
private SharedFileInputStream(
SharedFileInputStream parent,
long start,
long length)
throws IOException
{
super(new BufferedInputStream(new FileInputStream(parent._file)));
_parent = parent;
_file = parent._file;
_start = start;
_length = length;
in.skip(start);
}
public long getPosition()
{
return _position;
}
public InputStream newStream(long start, long finish)
{
try
{
SharedFileInputStream stream;
if (finish < 0)
{
if (_length > 0)
{
stream = new SharedFileInputStream(this, _start + start, _length - start);
}
else if (_length == 0)
{
stream = new SharedFileInputStream(this, _start + start, 0);
}
else
{
stream = new SharedFileInputStream(this, _start + start, -1);
}
}
else
{
stream = new SharedFileInputStream(this, _start + start, finish - start);
}
_subStreams.add(stream);
return stream;
}
catch (IOException e)
{
throw new IllegalStateException("unable to create shared stream: " + e);
}
}
public int read(
byte[] buf)
throws IOException
{
return this.read(buf, 0, buf.length);
}
public int read(
byte[] buf,
int off,
int len)
throws IOException
{
int count = 0;
if (len == 0)
{
return 0;
}
while (count < len)
{
int ch = this.read();
if (ch < 0)
{
break;
}
buf[off + count] = (byte)ch;
count++;
}
if (count == 0)
{
return -1; // EOF
}
return count;
}
public int read()
throws IOException
{
if (_position == _length)
{
return -1;
}
_position++;
return in.read();
}
public boolean markSupported()
{
return true;
}
public long skip(long n)
throws IOException
{
long count;
for (count = 0; count != n; count++)
{
if (this.read() < 0)
{
break;
}
}
return count;
}
public void mark(
int readLimit)
{
_markedPosition = _position;
in.mark(readLimit);
}
public void reset()
throws IOException
{
_position = _markedPosition;
in.reset();
}
/**
* Return the shared stream that represents the top most stream that
* this stream inherits from.
* @return the base of the shared stream tree.
*/
public SharedFileInputStream getRoot()
{
if (_parent != null)
{
return _parent.getRoot();
}
return this;
}
/**
* Close of this stream and any substreams that have been created from it.
* @throws IOException on problem closing the main stream.
*/
public void dispose()
throws IOException
{
Iterator it = _subStreams.iterator();
while (it.hasNext())
{
try
{
((SharedFileInputStream)it.next()).dispose();
}
catch (IOException e)
{
// ignore
}
}
in.close();
}
}

View File

@@ -0,0 +1,960 @@
package org.bouncycastle.mail.smime.validator;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.cert.jcajce.JcaCertStoreBuilder;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.i18n.ErrorBundle;
import org.bouncycastle.i18n.filter.TrustedInput;
import org.bouncycastle.i18n.filter.UntrustedInput;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.util.Integers;
import org.bouncycastle.x509.CertPathReviewerException;
import org.bouncycastle.x509.PKIXCertPathReviewer;
public class SignedMailValidator
{
private static final String RESOURCE_NAME = "org.bouncycastle.mail.smime.validator.SignedMailValidatorMessages";
private static final Class DEFAULT_CERT_PATH_REVIEWER = PKIXCertPathReviewer.class;
private static final String EXT_KEY_USAGE = X509Extensions.ExtendedKeyUsage
.getId();
private static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName
.getId();
private static final int shortKeyLength = 512;
// (365.25*30)*24*3600*1000
private static final long THIRTY_YEARS_IN_MILLI_SEC = 21915l*12l*3600l*1000l;
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
private CertStore certs;
private SignerInformationStore signers;
private Map results;
private String[] fromAddresses;
private Class certPathReviewerClass;
/**
* Validates the signed {@link MimeMessage} message. The
* {@link PKIXParameters} from param are used for the certificate path
* validation. The actual PKIXParameters used for the certificate path
* validation is a copy of param with the followin changes: <br> - The
* validation date is changed to the signature time <br> - A CertStore with
* certificates and crls from the mail message is added to the CertStores.<br>
* <br>
* In <code>param</code> it's also possible to add additional CertStores
* with intermediate Certificates and/or CRLs which then are also used for
* the validation.
*
* @param message
* the signed MimeMessage
* @param param
* the parameters for the certificate path validation
* @throws SignedMailValidatorException
* if the message is no signed message or if an exception occurs
* reading the message
*/
public SignedMailValidator(MimeMessage message, PKIXParameters param)
throws SignedMailValidatorException
{
this(message, param, DEFAULT_CERT_PATH_REVIEWER);
}
/**
* Validates the signed {@link MimeMessage} message. The
* {@link PKIXParameters} from param are used for the certificate path
* validation. The actual PKIXParameters used for the certificate path
* validation is a copy of param with the followin changes: <br> - The
* validation date is changed to the signature time <br> - A CertStore with
* certificates and crls from the mail message is added to the CertStores.<br>
* <br>
* In <code>param</code> it's also possible to add additional CertStores
* with intermediate Certificates and/or CRLs which then are also used for
* the validation.
*
* @param message
* the signed MimeMessage
* @param param
* the parameters for the certificate path validation
* @param certPathReviewerClass
* a subclass of {@link PKIXCertPathReviewer}. The SignedMailValidator
* uses objects of this type for the cert path vailidation. The class must
* have an empty constructor.
* @throws SignedMailValidatorException
* if the message is no signed message or if an exception occurs
* reading the message
* @throws IllegalArgumentException if the certPathReviewerClass is not a
* subclass of {@link PKIXCertPathReviewer} or objects of
* certPathReviewerClass can not be instantiated
*/
public SignedMailValidator(MimeMessage message, PKIXParameters param, Class certPathReviewerClass)
throws SignedMailValidatorException
{
this.certPathReviewerClass = certPathReviewerClass;
boolean isSubclass = DEFAULT_CERT_PATH_REVIEWER.isAssignableFrom(certPathReviewerClass);
if(!isSubclass)
{
throw new IllegalArgumentException("certPathReviewerClass is not a subclass of " + DEFAULT_CERT_PATH_REVIEWER.getName());
}
SMIMESigned s;
try
{
// check if message is multipart signed
if (message.isMimeType("multipart/signed"))
{
MimeMultipart mimemp = (MimeMultipart) message.getContent();
s = new SMIMESigned(mimemp);
}
else if (message.isMimeType("application/pkcs7-mime")
|| message.isMimeType("application/x-pkcs7-mime"))
{
s = new SMIMESigned(message);
}
else
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSignedMessage");
throw new SignedMailValidatorException(msg);
}
// save certstore and signerInformationStore
certs = new JcaCertStoreBuilder().addCertificates(s.getCertificates()).addCRLs(s.getCRLs()).setProvider("BC").build();
signers = s.getSignerInfos();
// save "from" addresses from message
Address[] froms = message.getFrom();
InternetAddress sender = null;
try
{
if(message.getHeader("Sender") != null)
{
sender = new InternetAddress(message.getHeader("Sender")[0]);
}
}
catch (MessagingException ex)
{
//ignore garbage in Sender: header
}
fromAddresses = new String[froms.length + (sender!=null?1:0)];
for (int i = 0; i < froms.length; i++)
{
InternetAddress inetAddr = (InternetAddress) froms[i];
fromAddresses[i] = inetAddr.getAddress();
}
if(sender!=null)
{
fromAddresses[froms.length] = sender.getAddress();
}
// initialize results
results = new HashMap();
}
catch (Exception e)
{
if (e instanceof SignedMailValidatorException)
{
throw (SignedMailValidatorException) e;
}
// exception reading message
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionReadingMessage",
new Object[] { e.getMessage(), e , e.getClass().getName()});
throw new SignedMailValidatorException(msg, e);
}
// validate signatues
validateSignatures(param);
}
protected void validateSignatures(PKIXParameters pkixParam)
{
PKIXParameters usedParameters = (PKIXParameters) pkixParam.clone();
// add crls and certs from mail
usedParameters.addCertStore(certs);
Collection c = signers.getSigners();
Iterator it = c.iterator();
// check each signer
while (it.hasNext())
{
List errors = new ArrayList();
List notifications = new ArrayList();
SignerInformation signer = (SignerInformation) it.next();
// signer certificate
X509Certificate cert = null;
try
{
Collection certCollection = findCerts(usedParameters
.getCertStores(), selectorConverter.getCertSelector(signer.getSID()));
Iterator certIt = certCollection.iterator();
if (certIt.hasNext())
{
cert = (X509Certificate) certIt.next();
}
}
catch (CertStoreException cse)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionRetrievingSignerCert",
new Object[] { cse.getMessage(), cse , cse.getClass().getName()});
errors.add(msg);
}
if (cert != null)
{
// check signature
boolean validSignature = false;
try
{
validSignature = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()));
if (!validSignature)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signatureNotVerified");
errors.add(msg);
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionVerifyingSignature",
new Object[] { e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
// check signer certificate (mail address, key usage, etc)
checkSignerCert(cert, errors, notifications);
// notify if a signed receip request is in the message
AttributeTable atab = signer.getSignedAttributes();
if (atab != null)
{
Attribute attr = atab.get(PKCSObjectIdentifiers.id_aa_receiptRequest);
if (attr != null)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signedReceiptRequest");
notifications.add(msg);
}
}
// check certificate path
// get signing time if possible, otherwise use current time as
// signing time
Date signTime = getSignatureTime(signer);
if (signTime == null) // no signing time was found
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSigningTime");
errors.add(msg);
signTime = new Date();
}
else
{
// check if certificate was valid at signing time
try
{
cert.checkValidity(signTime);
}
catch (CertificateExpiredException e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certExpired",
new Object[] { new TrustedInput(signTime), new TrustedInput(cert.getNotAfter()) });
errors.add(msg);
}
catch (CertificateNotYetValidException e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certNotYetValid",
new Object[] { new TrustedInput(signTime), new TrustedInput(cert.getNotBefore()) });
errors.add(msg);
}
}
usedParameters.setDate(signTime);
try
{
// construct cert chain
CertPath certPath;
List userProvidedList;
List userCertStores = new ArrayList();
userCertStores.add(certs);
Object[] cpres = createCertPath(cert, usedParameters.getTrustAnchors(), pkixParam.getCertStores(), userCertStores);
certPath = (CertPath) cpres[0];
userProvidedList = (List) cpres[1];
// validate cert chain
PKIXCertPathReviewer review;
try
{
review = (PKIXCertPathReviewer)certPathReviewerClass.newInstance();
}
catch (IllegalAccessException e)
{
throw new IllegalArgumentException("Cannot instantiate object of type " +
certPathReviewerClass.getName() + ": " + e.getMessage());
}
catch (InstantiationException e)
{
throw new IllegalArgumentException("Cannot instantiate object of type " +
certPathReviewerClass.getName() + ": " + e.getMessage());
}
review.init(certPath, usedParameters);
if (!review.isValidCertPath())
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certPathInvalid");
errors.add(msg);
}
results.put(signer, new ValidationResult(review,
validSignature, errors, notifications, userProvidedList));
}
catch (GeneralSecurityException gse)
{
// cannot create cert path
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.exceptionCreateCertPath",
new Object[] { gse.getMessage(), gse, gse.getClass().getName() });
errors.add(msg);
results.put(signer, new ValidationResult(null,
validSignature, errors, notifications, null));
}
catch (CertPathReviewerException cpre)
{
// cannot initialize certpathreviewer - wrong parameters
errors.add(cpre.getErrorMessage());
results.put(signer, new ValidationResult(null,
validSignature, errors, notifications, null));
}
}
else
// no signer certificate found
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noSignerCert");
errors.add(msg);
results.put(signer, new ValidationResult(null, false, errors,
notifications, null));
}
}
}
public static Set getEmailAddresses(X509Certificate cert) throws IOException, CertificateEncodingException
{
Set addresses = new HashSet();
X509Principal name = PrincipalUtil.getSubjectX509Principal(cert);
Vector oids = name.getOIDs();
Vector names = name.getValues();
for (int i = 0; i < oids.size(); i++)
{
if (oids.get(i).equals(X509Principal.EmailAddress))
{
String email = ((String) names.get(i)).toLowerCase();
addresses.add(email);
break;
}
}
byte[] ext = cert.getExtensionValue(SUBJECT_ALTERNATIVE_NAME);
if (ext != null)
{
ASN1Sequence altNames = ASN1Sequence.getInstance(getObject(ext));
for (int j = 0; j < altNames.size(); j++)
{
ASN1TaggedObject o = (ASN1TaggedObject) altNames
.getObjectAt(j);
if (o.getTagNo() == 1)
{
String email = DERIA5String.getInstance(o, false)
.getString().toLowerCase();
addresses.add(email);
}
}
}
return addresses;
}
private static ASN1Primitive getObject(byte[] ext) throws IOException
{
ASN1InputStream aIn = new ASN1InputStream(ext);
ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
aIn = new ASN1InputStream(octs.getOctets());
return aIn.readObject();
}
protected void checkSignerCert(X509Certificate cert, List errors,
List notifications)
{
// get key length
PublicKey key = cert.getPublicKey();
int keyLenght = -1;
if (key instanceof RSAPublicKey)
{
keyLenght = ((RSAPublicKey) key).getModulus().bitLength();
}
else if (key instanceof DSAPublicKey)
{
keyLenght = ((DSAPublicKey) key).getParams().getP().bitLength();
}
if (keyLenght != -1 && keyLenght <= shortKeyLength)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.shortSigningKey",
new Object[]{Integers.valueOf(keyLenght)});
notifications.add(msg);
}
// warn if certificate has very long validity period
long validityPeriod = cert.getNotAfter().getTime() - cert.getNotBefore().getTime();
if (validityPeriod > THIRTY_YEARS_IN_MILLI_SEC)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.longValidity",
new Object[] {new TrustedInput(cert.getNotBefore()), new TrustedInput(cert.getNotAfter())});
notifications.add(msg);
}
// check key usage if digitalSignature or nonRepudiation is set
boolean[] keyUsage = cert.getKeyUsage();
if (keyUsage != null && !keyUsage[0] && !keyUsage[1])
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.signingNotPermitted");
errors.add(msg);
}
// check extended key usage
try
{
byte[] ext = cert.getExtensionValue(EXT_KEY_USAGE);
if (ext != null)
{
ExtendedKeyUsage extKeyUsage = ExtendedKeyUsage
.getInstance(getObject(ext));
if (!extKeyUsage
.hasKeyPurposeId(KeyPurposeId.anyExtendedKeyUsage)
&& !extKeyUsage
.hasKeyPurposeId(KeyPurposeId.id_kp_emailProtection))
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.extKeyUsageNotPermitted");
errors.add(msg);
}
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.extKeyUsageError", new Object[] {
e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
// cert has an email address
try
{
Set certEmails = getEmailAddresses(cert);
if (certEmails.isEmpty())
{
// error no email address in signing certificate
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.noEmailInCert");
errors.add(msg);
}
else
{
// check if email in cert is equal to the from address in the
// message
boolean equalsFrom = false;
for (int i = 0; i < fromAddresses.length; i++)
{
if (certEmails.contains(fromAddresses[i].toLowerCase()))
{
equalsFrom = true;
break;
}
}
if (!equalsFrom)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.emailFromCertMismatch",
new Object[] {
new UntrustedInput(
addressesToString(fromAddresses)),
new UntrustedInput(certEmails) });
errors.add(msg);
}
}
}
catch (Exception e)
{
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.certGetEmailError", new Object[] {
e.getMessage(), e, e.getClass().getName() });
errors.add(msg);
}
}
static String addressesToString(Object[] a)
{
if (a == null)
{
return "null";
}
StringBuffer b = new StringBuffer();
b.append('[');
for (int i = 0; i != a.length; i++)
{
if (i > 0)
{
b.append(", ");
}
b.append(String.valueOf(a[i]));
}
return b.append(']').toString();
}
public static Date getSignatureTime(SignerInformation signer)
{
AttributeTable atab = signer.getSignedAttributes();
Date result = null;
if (atab != null)
{
Attribute attr = atab.get(CMSAttributes.signingTime);
if (attr != null)
{
Time t = Time.getInstance(attr.getAttrValues().getObjectAt(0)
.toASN1Primitive());
result = t.getDate();
}
}
return result;
}
private static List findCerts(List certStores, X509CertSelector selector)
throws CertStoreException
{
List result = new ArrayList();
Iterator it = certStores.iterator();
while (it.hasNext())
{
CertStore store = (CertStore) it.next();
Collection coll = store.getCertificates(selector);
result.addAll(coll);
}
return result;
}
private static X509Certificate findNextCert(List certStores, X509CertSelector selector, Set certSet)
throws CertStoreException
{
Iterator certIt = findCerts(certStores, selector).iterator();
boolean certFound = false;
X509Certificate nextCert = null;
while (certIt.hasNext())
{
nextCert = (X509Certificate) certIt.next();
if (!certSet.contains(nextCert))
{
certFound = true;
break;
}
}
return certFound ? nextCert : null;
}
/**
*
* @param signerCert the end of the path
* @param trustanchors trust anchors for the path
* @param certStores
* @return the resulting certificate path.
* @throws GeneralSecurityException
*/
public static CertPath createCertPath(X509Certificate signerCert,
Set trustanchors, List certStores) throws GeneralSecurityException
{
Object[] results = createCertPath(signerCert, trustanchors, certStores, null);
return (CertPath) results[0];
}
/**
* Returns an Object array containing a CertPath and a List of Booleans. The list contains the value <code>true</code>
* if the corresponding certificate in the CertPath was taken from the user provided CertStores.
* @param signerCert the end of the path
* @param trustanchors trust anchors for the path
* @param systemCertStores list of {@link CertStore} provided by the system
* @param userCertStores list of {@link CertStore} provided by the user
* @return a CertPath and a List of booleans.
* @throws GeneralSecurityException
*/
public static Object[] createCertPath(X509Certificate signerCert,
Set trustanchors, List systemCertStores, List userCertStores) throws GeneralSecurityException
{
Set certSet = new LinkedHashSet();
List userProvidedList = new ArrayList();
// add signer certificate
X509Certificate cert = signerCert;
certSet.add(cert);
userProvidedList.add(new Boolean(true));
boolean trustAnchorFound = false;
X509Certificate taCert = null;
// add other certs to the cert path
while (cert != null && !trustAnchorFound)
{
// check if cert Issuer is Trustanchor
Iterator trustIt = trustanchors.iterator();
while (trustIt.hasNext())
{
TrustAnchor anchor = (TrustAnchor) trustIt.next();
X509Certificate anchorCert = anchor.getTrustedCert();
if (anchorCert != null)
{
if (anchorCert.getSubjectX500Principal().equals(
cert.getIssuerX500Principal()))
{
try
{
cert.verify(anchorCert.getPublicKey(), "BC");
trustAnchorFound = true;
taCert = anchorCert;
break;
}
catch (Exception e)
{
// trustanchor not found
}
}
}
else
{
if (anchor.getCAName().equals(
cert.getIssuerX500Principal().getName()))
{
try
{
cert.verify(anchor.getCAPublicKey(), "BC");
trustAnchorFound = true;
break;
}
catch (Exception e)
{
// trustanchor not found
}
}
}
}
if (!trustAnchorFound)
{
// add next cert to path
X509CertSelector select = new X509CertSelector();
try
{
select.setSubject(cert.getIssuerX500Principal().getEncoded());
}
catch (IOException e)
{
throw new IllegalStateException(e.toString());
}
byte[] authKeyIdentBytes = cert.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
if (authKeyIdentBytes != null)
{
try
{
AuthorityKeyIdentifier kid = AuthorityKeyIdentifier.getInstance(getObject(authKeyIdentBytes));
if (kid.getKeyIdentifier() != null)
{
select.setSubjectKeyIdentifier(new DEROctetString(kid.getKeyIdentifier()).getEncoded(ASN1Encoding.DER));
}
}
catch (IOException ioe)
{
// ignore
}
}
boolean userProvided = false;
cert = findNextCert(systemCertStores, select, certSet);
if (cert == null && userCertStores != null)
{
userProvided = true;
cert = findNextCert(userCertStores, select, certSet);
}
if (cert != null)
{
// cert found
certSet.add(cert);
userProvidedList.add(new Boolean(userProvided));
}
}
}
// if a trustanchor was found - try to find a selfsigned certificate of
// the trustanchor
if (trustAnchorFound)
{
if (taCert != null && taCert.getSubjectX500Principal().equals(taCert.getIssuerX500Principal()))
{
certSet.add(taCert);
userProvidedList.add(new Boolean(false));
}
else
{
X509CertSelector select = new X509CertSelector();
try
{
select.setSubject(cert.getIssuerX500Principal().getEncoded());
select.setIssuer(cert.getIssuerX500Principal().getEncoded());
}
catch (IOException e)
{
throw new IllegalStateException(e.toString());
}
boolean userProvided = false;
taCert = findNextCert(systemCertStores, select, certSet);
if (taCert == null && userCertStores != null)
{
userProvided = true;
taCert = findNextCert(userCertStores, select, certSet);
}
if (taCert != null)
{
try
{
cert.verify(taCert.getPublicKey(), "BC");
certSet.add(taCert);
userProvidedList.add(new Boolean(userProvided));
}
catch (GeneralSecurityException gse)
{
// wrong cert
}
}
}
}
CertPath certPath = CertificateFactory.getInstance("X.509", "BC").generateCertPath(new ArrayList(certSet));
return new Object[] {certPath, userProvidedList};
}
public CertStore getCertsAndCRLs()
{
return certs;
}
public SignerInformationStore getSignerInformationStore()
{
return signers;
}
public ValidationResult getValidationResult(SignerInformation signer)
throws SignedMailValidatorException
{
if (signers.getSigners(signer.getSID()).isEmpty())
{
// the signer is not part of the SignerInformationStore
// he has not signed the message
ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,
"SignedMailValidator.wrongSigner");
throw new SignedMailValidatorException(msg);
}
else
{
return (ValidationResult) results.get(signer);
}
}
public class ValidationResult
{
private PKIXCertPathReviewer review;
private List errors;
private List notifications;
private List userProvidedCerts;
private boolean signVerified;
ValidationResult(PKIXCertPathReviewer review, boolean verified,
List errors, List notifications, List userProvidedCerts)
{
this.review = review;
this.errors = errors;
this.notifications = notifications;
signVerified = verified;
this.userProvidedCerts = userProvidedCerts;
}
/**
* Returns a list of error messages of type {@link ErrorBundle}.
*
* @return List of error messages
*/
public List getErrors()
{
return errors;
}
/**
* Returns a list of notification messages of type {@link ErrorBundle}.
*
* @return List of notification messages
*/
public List getNotifications()
{
return notifications;
}
/**
*
* @return the PKIXCertPathReviewer for the CertPath of this signature
* or null if an Exception occured.
*/
public PKIXCertPathReviewer getCertPathReview()
{
return review;
}
/**
*
* @return the CertPath for this signature
* or null if an Exception occured.
*/
public CertPath getCertPath()
{
return review != null ? review.getCertPath() : null;
}
/**
*
* @return a List of Booleans that are true if the corresponding certificate in the CertPath was taken from
* the CertStore of the SMIME message
*/
public List getUserProvidedCerts()
{
return userProvidedCerts;
}
/**
*
* @return true if the signature corresponds to the public key of the
* signer
*/
public boolean isVerifiedSignature()
{
return signVerified;
}
/**
*
* @return true if the signature is valid (ie. if it corresponds to the
* public key of the signer and the cert path for the signers
* certificate is also valid)
*/
public boolean isValidSignature()
{
if (review != null)
{
return signVerified && review.isValidCertPath()
&& errors.isEmpty();
}
else
{
return false;
}
}
}
}

View File

@@ -0,0 +1,19 @@
package org.bouncycastle.mail.smime.validator;
import org.bouncycastle.i18n.ErrorBundle;
import org.bouncycastle.i18n.LocalizedException;
public class SignedMailValidatorException extends LocalizedException
{
public SignedMailValidatorException(ErrorBundle errorMessage, Throwable throwable)
{
super(errorMessage, throwable);
}
public SignedMailValidatorException(ErrorBundle errorMessage)
{
super(errorMessage);
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body bgcolor="#ffffff">
Example code demonstrating the use of the S/MIME package for a variety of uses.
</body>
</html>

View File

@@ -0,0 +1,5 @@
<html>
<body bgcolor="#ffffff">
S/MIME handlers for the JavaMail API.
</body>
</html>

View File

@@ -0,0 +1,11 @@
<html>
<body bgcolor="#ffffff">
High level classes for dealing with S/MIME objects (RFC 3851).
<p>
There is one thing that is worth commenting about with these. If you're using
AS2 on some other standard which specifies a different default content transfer encoding from RFC 2405, make
sure you use the constructors on SMIMESigned and SMIMESignedGenerator that allow you to
set the default ("binary" in the case of AS2 as opposed to "bit7" which is the default).
</p>
</body>
</html>

View File

@@ -0,0 +1,172 @@
## constructor exception messages
# Signature valid
SignedMailValidator.sigValid.title = Signature valid
SignedMailValidator.sigValid.text = Signature valid
SignedMailValidator.sigValid.summary = Signature valid
SignedMailValidator.sigValid.details = Signature valid
# Signature invalid
SignedMailValidator.sigInvalid.title = Signature invalid
SignedMailValidator.sigInvalid.text = Signature invalid
SignedMailValidator.sigInvalid.summary = Signature invalid
SignedMailValidator.sigInvalid.details = Signature invalid
# message is not signed
SignedMailValidator.noSignedMessage.title = Message is not signed
SignedMailValidator.noSignedMessage.text = SignedMailValidator: MimeMessage message is not a signed message.
SignedMailValidator.noSignedMessage.summary = SignedMailValidator: MimeMessage message is not a signed message.
SignedMailValidator.noSignedMessage.details = SignedMailValidator: MimeMessage message is not a signed message.
# exception reading the Mime message
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionReadingMessage.title = Exception reading the MimeMessage
SignedMailValidator.exceptionReadingMessage.text = SignedMailValidator: there was a {2} reading the MimeMessage: {0}.
SignedMailValidator.exceptionReadingMessage.summary = SignedMailValidator: there was a {2} reading the MimeMessage: {0}.
SignedMailValidator.exceptionReadingMessage.details = SignedMailValidator: there was a {2} reading the MimeMessage: {0}.
## exception messages
# signer has not signed the mail message
SignedMailValidator.wrongSigner.title = Signer has not signed the message
SignedMailValidator.wrongSigner.text = The given signer did not sign the mail message.
SignedMailValidator.wrongSigner.summary = The given signer did not sign the mail message.
SignedMailValidator.wrongSigner.details = The given signer did not sign the mail message.
## notifications messages
# short signing key
# {0} the key lenght as Integer
SignedMailValidator.shortSigningKey.title = Careless short signing key
SignedMailValidator.shortSigningKey.text = Warning: The signing key is only {0} bits long.
SignedMailValidator.shortSigningKey.summary = Warning: The signing key is only {0} bits long.
SignedMailValidator.shortSigningKey.details = Warning: The signing key is only {0} bits long.
# signing certificate has very long validity period
# {0} notBefore date
# {1} notAfter date
SignedMailValidator.longValidity.title = Very long validity period
SignedMailValidator.longValidity.text = Warning: The signing certificate has a very long validity period: from {0,date} {0,time,full} until {1,date} {1,time,full}.
SignedMailValidator.longValidity.summary = Warning: The signing certificate has a very long validity period: from {0,date} {0,time,full} until {1,date} {1,time,full}.
SignedMailValidator.longValidity.details = Warning: The signing certificate has a very long validity period: from {0,date} {0,time,full} until {1,date} {1,time,full}.
# signed receipt requested
SignedMailValidator.signedReceiptRequest.title = Signed Receipt Request
SignedMailValidator.signedReceiptRequest.text = The signature contains a signed receipt request.
SignedMailValidator.signedReceiptRequest.summary = The signature contains a signed receipt request.
SignedMailValidator.signedReceiptRequest.details = The signature contains a signed receipt request as per RFC 2634.
## error messages
# no signer certificate found
SignedMailValidator.noSignerCert.title = No signer certificate found
SignedMailValidator.noSignerCert.text = Signature Validation failed: No signer certificate found.
SignedMailValidator.noSignerCert.summary = Signature Validation failed: No signer certificate found.
SignedMailValidator.noSignerCert.details = Signature Validation failed: No signer certificate found.
# certificate contains no email address
SignedMailValidator.noEmailInCert.title = Certificate not usable for email signatures
SignedMailValidator.noEmailInCert.text = The signer certificate is not usable for email signatures: it contains no email address.
SignedMailValidator.noEmailInCert.summary = The signer certificate is not usable for email signatures: it contains no email address.
SignedMailValidator.noEmailInCert.details = The signer certificate is not usable for email signatures: it contains no email address.
# certificate email address does not match from email address
# {0} from email addresses in the message
# {1} email addresses in the certificate
SignedMailValidator.emailFromCertMismatch.title = Email address mismatch
SignedMailValidator.emailFromCertMismatch.text = Email address in singer certificate does not match the sender address. Signer email: {1}. Sender email: {0}.
SignedMailValidator.emailFromCertMismatch.summary = Email address in singer certificate does not match the sender address. Signer email: {1}. Sender email: {0}.
SignedMailValidator.emailFromCertMismatch.details = Email address in singer certificate does not match the sender address. Signer email: {1}. Sender email: {0}.
# exception extracting email addresses from certificate
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.certGetEmailError.title = Exception extracting email addresses from certificate
SignedMailValidator.certGetEmailError.text = There was a {2} extracting the email addresses from the certificate. Cause: {0}.
SignedMailValidator.certGetEmailError.summary = There was a {2} extracting the email addresses from the certificate.
SignedMailValidator.certGetEmailError.details = There was a {2} extracting the email addresses from the certificate. Cause: {0}.
# no signing time found
SignedMailValidator.noSigningTime.title = No signing time
SignedMailValidator.noSigningTime.text = The signature contains no signing time. Using the current time for validating the certificate path.
SignedMailValidator.noSigningTime.summary = The signature contains no signing time.
SignedMailValidator.noSigningTime.details = The signature contains no signing time. Using the current time for validating the certificate path.
# expired at signing time
# {0} signing time
# {1} not after date
SignedMailValidator.certExpired.title = Certificate expired at signing time
SignedMailValidator.certExpired.text = The message was signed at {0,date} {0,time,full}. But the certificate expired at {1,date} {1,time,full}.
SignedMailValidator.certExpired.summary = The message was signed at {0,date} {0,time,full}. But the certificate expired at {1,date} {1,time,full}.
SignedMailValidator.certExpired.details = The message was signed at {0,date} {0,time,full}. But the certificate expired at {1,date} {1,time,full}.
# not yet valid at signing time
# {0} signing time
# {1} notBefore date
SignedMailValidator.certNotYetValid.title = Certificate not yet valid at signing time
SignedMailValidator.certNotYetValid.text = The message was signed at {0,date} {0,time,full}. But the certificate is not valid before {1,date} {1,time,full}.
SignedMailValidator.certNotYetValid.summary = The message was signed at {0,date} {0,time,full}. But the certificate is not valid before {1,date} {1,time,full}.
SignedMailValidator.certNotYetValid.details = The message was signed at {0,date} {0,time,full}. But the certificate is not valid before {1,date} {1,time,full}.
# exception retrieving the signer certificate
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionRetrievingSignerCert.title = Exception retrieving the signer certificate
SignedMailValidator.exceptionRetrievingSignerCert.text = Signature Validation failed. There was a {2} retrieving the signer certificate: {0}.
SignedMailValidator.exceptionRetrievingSignerCert.summary = Signature Validation failed. There was a {2} retrieving the signer certificate.
SignedMailValidator.exceptionRetrievingSignerCert.details = Signature Validation failed There was a {2} retrieving the signer certificate: {0}.
# exception verifying the signature
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionVerifyingSignature.title = Signature not verified
SignedMailValidator.exceptionVerifyingSignature.text = Signature not verified. There was a {2}. Cause: {0}.
SignedMailValidator.exceptionVerifyingSignature.summary = Signature not verified. There was a {2}.
SignedMailValidator.exceptionVerifyingSignature.details = Signature not verified. There was a {2}. Cause: {0}.
# signature not verified
SignedMailValidator.signatureNotVerified.title = Signature not verified
SignedMailValidator.signatureNotVerified.text = Signature not verified. The public key of the signer does not correspond to the signature.
SignedMailValidator.signatureNotVerified.summary = Signature not verified. The public key of the signer does not correspond to the signature.
SignedMailValidator.signatureNotVerified.details = Signature not verified. The public key of the signer does not correspond to the signature.
# certificate key usage does not permit digitalSignature or nonRepudiation
SignedMailValidator.signingNotPermitted.title = Key not usable for email signatures
SignedMailValidator.signingNotPermitted.text = The key usage extension of signer certificate does not permit using the key for email signatures.
SignedMailValidator.signingNotPermitted.summary = The signer key is not usable for email signatures.
SignedMailValidator.signingNotPermitted.details = The key usage extension of signer certificate does not permit using the key for email signatures.
# certificate extended key usage does not permit emailProtection or anyExtendedKeyUsage
SignedMailValidator.extKeyUsageNotPermitted.title = Key not usable for email signatures
SignedMailValidator.extKeyUsageNotPermitted.text = The extended key usage extension of the signer certificate does not permit using the key for email signatures.
SignedMailValidator.extKeyUsageNotPermitted.summary = The signer key is not usable for email signatures.
SignedMailValidator.extKeyUsageNotPermitted.details = The extended key usage extension of the signer certificate does not permit using the key for email signatures.
# exception processing the extended key usage extension
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.extKeyUsageError.title = Exception processing the extended key usage extension
SignedMailValidator.extKeyUsageError.text = There was a {2} processing the extended key usage extension. Cause: {0}.
SignedMailValidator.extKeyUsageError.summary = There was a {2} processing the extended key usage extension.
SignedMailValidator.extKeyUsageError.details = There was a {2} processing the extended key usage extension. Cause: {0}.
# cannot create certificate path (exception)
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionCreateCertPath.title = Certificate path validation failed
SignedMailValidator.exceptionCreateCertPath.text = Certificate path validation failed. There was a {2} creating the CertPath: {0}.
SignedMailValidator.exceptionCreateCertPath.summary = Certificate path validation failed. There was a {2} creating the CertPath: {0}.
SignedMailValidator.exceptionCreateCertPath.details = Certificate path validation failed. There was a {2} creating the CertPath: {0}.
# certificate path is invalid
SignedMailValidator.certPathInvalid.title = Certificate path invalid
SignedMailValidator.certPathInvalid.text = The certificate path is invalid.
SignedMailValidator.certPathInvalid.summary = The certificate path is invalid.
SignedMailValidator.certPathInvalid.details = The certificate path is invalid.

View File

@@ -0,0 +1,172 @@
## constructor exception messages
# Signatur gültig
SignedMailValidator.sigValid.title = Signatur gültig
SignedMailValidator.sigValid.text = Signatur gültig
SignedMailValidator.sigValid.summary = Signatur gültig
SignedMailValidator.sigValid.details = Signatur gültig
# Signatur ungültig
SignedMailValidator.sigInvalid.title = Signatur ungültig
SignedMailValidator.sigInvalid.text = Signatur ungültig
SignedMailValidator.sigInvalid.summary = Signatur ungültig
SignedMailValidator.sigInvalid.details = Signatur ungültig
# message is not signed
SignedMailValidator.noSignedMessage.title = Die Nachricht ist nicht signiert
SignedMailValidator.noSignedMessage.text = SignedMailValidator: Die MimeMessage message ist nicht signiert.
SignedMailValidator.noSignedMessage.summary = SignedMailValidator: Die MimeMessage message ist nicht signiert.
SignedMailValidator.noSignedMessage.details = SignedMailValidator: Die MimeMessage message ist nicht signiert.
# exception reading the Mime message
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionReadingMessage.title = Fehler beim lesen der MimeMessage
SignedMailValidator.exceptionReadingMessage.text = SignedMailValidator: Es gab eine {2} beim lesen der MimeMessage: {0}.
SignedMailValidator.exceptionReadingMessage.summary = SignedMailValidator: Es gab eine {2} beim lesen der MimeMessage.
SignedMailValidator.exceptionReadingMessage.details = SignedMailValidator: Es gab eine {2} beim lesen der MimeMessage: {0}.
## exception messages
# signer has not signed the mail message
SignedMailValidator.wrongSigner.title = Falscher Unterzeichner
SignedMailValidator.wrongSigner.text = Die Email enhält keine Signatur vom gegebenen Unterzeichner.
SignedMailValidator.wrongSigner.summary = Die Email enhält keine Signatur vom gegebenen Unterzeichner.
SignedMailValidator.wrongSigner.details = Die Email enhält keine Signatur vom gegebenen Unterzeichner.
## notifications messages
# short signing key
# {0} the key lenght as Integer
SignedMailValidator.shortSigningKey.title = Fahrlässig kurzer Signaturschlüssel
SignedMailValidator.shortSigningKey.text = Warnung: Der Signaturschlüssel ist nur {0} bit lang.
SignedMailValidator.shortSigningKey.summary = Warnung: Der Signaturschlüssel ist nur {0} bit lang.
SignedMailValidator.shortSigningKey.details = Warnung: Der Signaturschlüssel ist nur {0} bit lang.
# signing certificate has very long validity period
# {0} notBefore date
# {1} notAfter date
SignedMailValidator.longValidity.title = Sehr lange Gültigkeitsdauer
SignedMailValidator.longValidity.text = Warnung: Das Signierzertifikat hat eine sehr lange Gültigkeitsdauer: von {0,date} {0,time,full} bis {1,date} {1,time,full}.
SignedMailValidator.longValidity.summary = Warnung: Das Signierzertifikat hat eine sehr lange Gültigkeitsdauer: von {0,date} {0,time,full} bis {1,date} {1,time,full}.
SignedMailValidator.longValidity.details = Warnung: Das Signierzertifikat hat eine sehr lange Gültigkeitsdauer: von {0,date} {0,time,full} bis {1,date} {1,time,full}.
# signed receipt requested
SignedMailValidator.signedReceiptRequest.title = Signed Receipt Request
SignedMailValidator.signedReceiptRequest.text = Die Signatur enthält einen signed receipt request.
SignedMailValidator.signedReceiptRequest.summary = Die Signatur enthält einen signed receipt request.
SignedMailValidator.signedReceiptRequest.details = Die Signatur enthält einen signed receipt request gemäss RFC 2634.
## error messages
# no signer certificate found
SignedMailValidator.noSignerCert.title = Kein Unterzeichner Zertifikat gefunden
SignedMailValidator.noSignerCert.text = Signatur Validierung fehlgeschlagen: Es wurde kein Unterzeichner Zertifikat gefunden.
SignedMailValidator.noSignerCert.summary = Signatur Validierung fehlgeschlagen: Es wurde kein Unterzeichner Zertifikat gefunden.
SignedMailValidator.noSignerCert.details = Signatur Validierung fehlgeschlagen: Es wurde kein Unterzeichner Zertifikat gefunden.
# certificate contains no email address
SignedMailValidator.noEmailInCert.title = Zertifikat nicht für Email Signaturen verwendbar
SignedMailValidator.noEmailInCert.text = Das Unterzeichner Zertifikat kann nicht für Email Signaturen verwendet werden: Es enthält keine Email Addresse.
SignedMailValidator.noEmailInCert.summary = Das Unterzeichner Zertifikat kann nicht für Email Signaturen verwendet werden: Es enthält keine Email Addresse.
SignedMailValidator.noEmailInCert.details = Das Unterzeichner Zertifikat kann nicht für Email Signaturen verwendet werden: Es enthält keine Email Addresse.
# certificate email address does not match from email address
# {0} from email addresses in the message
# {1} email addresses in the certificate
SignedMailValidator.emailFromCertMismatch.title = Email Addressen stimmen nicht überein
SignedMailValidator.emailFromCertMismatch.text = Die Email Addresse im Unterzeichner Zertifikat stimmt nicht mit der Sender Addresse überein. Unterzeichner: {1}. Sender: {0}.
SignedMailValidator.emailFromCertMismatch.summary = Die Email Addresse im Unterzeichner Zertifikat stimmt nicht mit der Sender Addresse überein. Unterzeichner: {1}. Sender: {0}.
SignedMailValidator.emailFromCertMismatch.details = Die Email Addresse im Unterzeichner Zertifikat stimmt nicht mit der Sender Addresse überein. Unterzeichner: {1}. Sender: {0}.
# exception extracting email addresses from certificate
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.certGetEmailError.title = Fehler bei extrahieren der Email Addresse vom Zertifikat
SignedMailValidator.certGetEmailError.text = Es gab eine {2} beim Extrahieren der Email Addresse vom Zertifikat. Grund: {0}.
SignedMailValidator.certGetEmailError.summary = Es gab eine {2} beim Extrahieren der Email Addresse vom Zertifikat.
SignedMailValidator.certGetEmailError.details = Es gab eine {2} beim Extrahieren der Email Addresse vom Zertifikat. Grund: {0}.
# no signing time found
SignedMailValidator.noSigningTime.title = Keine Signierzeit
SignedMailValidator.noSigningTime.text = Die Signatur enthält keine Signier Zeit. Benutze die aktuelle Zeit zur Zertifikationpfad Validierung.
SignedMailValidator.noSigningTime.summary = Die Signatur enthält keine Signier Zeit.
SignedMailValidator.noSigningTime.details = Die Signatur enthält keine Signier Zeit. Benutze die aktuelle Zeit zur Zertifikationpfad Validierung.
# expired at signing time
# {0} signing time
# {1} not after date
SignedMailValidator.certExpired.title = Zertifikat zur Signierzeit abgelaufen
SignedMailValidator.certExpired.text = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist am {1,date} {1,time,full} abgelaufen.
SignedMailValidator.certExpired.summary = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist am {1,date} {1,time,full} abgelaufen.
SignedMailValidator.certExpired.details = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist am {1,date} {1,time,full} abgelaufen.
# not yet valid at signing time
# {0} signing time
# {1} notBefore date
SignedMailValidator.certNotYetValid.title = Zertifikat noch nicht gültig zur Signierzeit
SignedMailValidator.certNotYetValid.text = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist erst gültig ab {1,date} {1,time,full}.
SignedMailValidator.certNotYetValid.summary = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist erst gültig ab {1,date} {1,time,full}.
SignedMailValidator.certNotYetValid.details = Die Nachricht wurde am {0,date} {0,time,full} signiert. Aber das Zertifikat ist erst gültig ab {1,date} {1,time,full}.
# exception retrieving the signer certificate
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionRetrievingSignerCert.title = Fehler beim Lesen des Signaturzertifikats
SignedMailValidator.exceptionRetrievingSignerCert.text = Signatur Validierung fehlgeschlagen. Es gab eine {2} beim Lesen des Signaturzertifikats: {0}.
SignedMailValidator.exceptionRetrievingSignerCert.summary = Signatur Validierung fehlgeschlagen. Es gab eine {2} beim Lesen des Signaturzertifikats.
SignedMailValidator.exceptionRetrievingSignerCert.details = Signatur Validierung fehlgeschlagen. Es gab eine {2} beim Lesen des Signaturzertifikats: {0}.
# exception verifying the signature
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionVerifyingSignature.title = Signatur nicht verifiziert
SignedMailValidator.exceptionVerifyingSignature.text = Signatur nicht verifiziert. Es gab eine {2}. Grund: {0}.
SignedMailValidator.exceptionVerifyingSignature.summary = Signatur nicht verifiziert. Es gab eine {2}.
SignedMailValidator.exceptionVerifyingSignature.details = Signatur nicht verifiziert. Es gab eine {2}. Grund: {0}.
# signature not verified
SignedMailValidator.signatureNotVerified.title = Signatur nicht verifiziert
SignedMailValidator.signatureNotVerified.text = Signatur nicht verifiziert. Der öffentliche Schlüssel des Unterzeichners passt nicht zur Signatur.
SignedMailValidator.signatureNotVerified.summary = Signatur nicht verifiziert. Der öffentliche Schlüssel des Unterzeichners passt nicht zur Signatur.
SignedMailValidator.signatureNotVerified.details = Signatur nicht verifiziert. Der öffentliche Schlüssel des Unterzeichners passt nicht zur Signatur.
# certificate key usage does not permit digitalSignature or nonRepudiation
SignedMailValidator.signingNotPermitted.title = Schlüssel nicht verwendbar für Email Signaturen
SignedMailValidator.signingNotPermitted.text = Der Schlüssel des Unterzeichners darf nicht für Email Signaturen verwendet werden.
SignedMailValidator.signingNotPermitted.summary = Der Schlüssel des Unterzeichners darf nicht für Email Signaturen verwendet werden.
SignedMailValidator.signingNotPermitted.details = Die Schlüsselverwendung des Unterzeichner Zertifikats erlaubt keine Verwendung für Email Signaturen.
# certificate extended key usage does not permit emailProtection or anyExtendedKeyUsage
SignedMailValidator.extKeyUsageNotPermitted.title = Schlüssel nicht verwendbar für Email Signaturen
SignedMailValidator.extKeyUsageNotPermitted.text = Der Schlüssel des Unterzeichners darf nicht für Email Signaturen verwendet werden.
SignedMailValidator.extKeyUsageNotPermitted.summary = Der Schlüssel des Unterzeichners darf nicht für Email Signaturen verwendet werden.
SignedMailValidator.extKeyUsageNotPermitted.details = Die erweiterte Schlüsselverwendung des Unterzeichner Zertifikats erlaubt keine Verwendung für Email Signaturen.
# exception processing the extended key usage extension
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.extKeyUsageError.title = Fehler bei der Verarbeitung der Extended key usage Erweiterung
SignedMailValidator.extKeyUsageError.text = Es gab eine {2} bei der Verarbeitung der Extended key usage Erweiterung. Grund: {0}.
SignedMailValidator.extKeyUsageError.summary = Es gab eine {2} bei der Verarbeitung der Extended key usage Erweiterung.
SignedMailValidator.extKeyUsageError.details = Es gab eine {2} bei der Verarbeitung der Extended key usage Erweiterung. Grund: {0}.
# cannot create certificate path (exception)
# {0} message of the underlying exception
# {1} the underlying exception
# {2} the name of the exception
SignedMailValidator.exceptionCreateCertPath.title = Zertifizierungspfad Validierung fehlgeschlagen
SignedMailValidator.exceptionCreateCertPath.text = Die Zertifizierungspfad Validierung ist fehlgeschlagen. Es gab eine {2} beim erstellen des Zertifizierungspfad: {0}.
SignedMailValidator.exceptionCreateCertPath.summary = Die Zertifizierungspfad Validierung ist fehlgeschlagen. Es gab eine {2} beim erstellen des Zertifizierungspfad: {0}.
SignedMailValidator.exceptionCreateCertPath.details = Die Zertifizierungspfad Validierung ist fehlgeschlagen. Es gab eine {2} beim erstellen des Zertifizierungspfad: {0}.
# certificate path is invalid
SignedMailValidator.certPathInvalid.title = Zertifikats-Pfad ungültig
SignedMailValidator.certPathInvalid.text = Der Zertifikats-Pfad ist ungültig.
SignedMailValidator.certPathInvalid.summary = Der Zertifikats-Pfad ist ungültig.
SignedMailValidator.certPathInvalid.details = Der Zertifikats-Pfad ist ungültig.

View File

@@ -0,0 +1,26 @@
package org.bouncycastle.mail.smime.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class AllTests
{
public static void main (String[] args)
throws Exception
{
junit.textui.TestRunner.run (suite());
}
public static Test suite()
throws Exception
{
TestSuite suite= new TestSuite("SMIME tests");
suite.addTest(NewSMIMESignedTest.suite());
suite.addTest(SignedMailValidatorTest.suite());
suite.addTest(NewSMIMEEnvelopedTest.suite());
suite.addTest(SMIMECompressedTest.suite());
suite.addTest(SMIMEMiscTest.suite());
return suite;
}
}

View File

@@ -0,0 +1,8 @@
package org.bouncycastle.mail.smime.test;
import org.bouncycastle.x509.PKIXCertPathReviewer;
public class DummyCertPathReviewer extends PKIXCertPathReviewer
{
}

View File

@@ -0,0 +1,511 @@
package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
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.CMSAlgorithm;
import org.bouncycastle.cms.KeyTransRecipientId;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMEEnvelopedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
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;
public class NewSMIMEEnvelopedTest
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 boolean _initialised = false;
private static final byte[] testMessage = Base64.decode(
"TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
"T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
"4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
"wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
"ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
"mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
"1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
"wMTMyLS0NCg==");
private static void init()
throws Exception
{
if (!_initialised)
{
_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);
}
}
public NewSMIMEEnvelopedTest(
String name)
{
super(name);
}
public static void main(
String args[])
{
junit.textui.TestRunner.run(NewSMIMEEnvelopedTest.class);
}
public static Test suite()
throws Exception
{
return new SMIMETestSetup(new TestSuite(NewSMIMEEnvelopedTest.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");
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
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.DES_EDE3_CBC).setProvider(BC).build());
assertEquals("application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-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 testDESEDE3Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.DES_EDE3_CBC;
verifyAlgorithm(algorithm, msg);
}
public void testParserDESEDE3Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.DES_EDE3_CBC;
verifyParserAlgorithm(algorithm, msg);
}
public void testIDEAEncrypted()
throws Exception
{
if (isPresent("IDEA"))
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.IDEA_CBC;
verifyAlgorithm(algorithm, msg);
}
}
private boolean isPresent(String algorithm)
throws Exception
{
try
{
Cipher.getInstance(algorithm, BC);
return true;
}
catch (NoSuchAlgorithmException e)
{
return false;
}
}
public void testRC2Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.RC2_CBC;
verifyAlgorithm(algorithm, msg);
}
public void testCASTEncrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.CAST5_CBC;
verifyAlgorithm(algorithm, msg);
}
public void testAES128Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.AES128_CBC;
verifyAlgorithm(algorithm, msg);
}
public void testAES192Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.AES192_CBC;
verifyAlgorithm(algorithm, msg);
}
public void testAES256Encrypted()
throws Exception
{
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
String algorithm = SMIMEEnvelopedGenerator.AES256_CBC;
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)));
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");
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(_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.RC2_CBC, 40).setProvider(BC).build());
SMIMEEnveloped m = new SMIMEEnveloped(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)));
verifyMessageBytes(msg, res);
}
public void testTwoRecipients()
throws Exception
{
MimeBodyPart _msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
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.RC2_CBC, 40).setProvider(BC).build());
SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(mp);
RecipientId recId = getRecipientId(_reciCert2);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipient = recipients.get(recId);
FileBackedMimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransEnvelopedRecipient(_reciKP2.getPrivate()).setProvider(BC)));
verifyMessageBytes(_msg, res);
m = new SMIMEEnvelopedParser(mp);
res.dispose();
recId = getRecipientId(_reciCert);
recipients = m.getRecipientInfos();
recipient = recipients.get(recId);
res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
verifyMessageBytes(_msg, res);
res.dispose();
}
private void verifyAlgorithm(
String algorithmOid,
MimeBodyPart msg)
throws Exception
{
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
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());
SMIMEEnveloped m = new SMIMEEnveloped(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)));
verifyMessageBytes(msg, res);
}
private void verifyParserAlgorithm(
String algorithmOid,
MimeBodyPart msg)
throws Exception
{
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
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());
SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(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)));
verifyMessageBytes(msg, res);
}
private RecipientId getRecipientId(
X509Certificate cert)
throws IOException, CertificateEncodingException
{
RecipientId recId = new JceKeyTransRecipientId(cert);
return recId;
}
private void verifyMessageBytes(MimeBodyPart a, MimeBodyPart b)
throws IOException, MessagingException
{
ByteArrayOutputStream _baos = new ByteArrayOutputStream();
a.writeTo(_baos);
_baos.close();
byte[] _msgBytes = _baos.toByteArray();
_baos = new ByteArrayOutputStream();
b.writeTo(_baos);
_baos.close();
byte[] _resBytes = _baos.toByteArray();
assertEquals(true, Arrays.equals(_msgBytes, _resBytes));
}
}

View File

@@ -0,0 +1,228 @@
package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.cms.jcajce.ZlibCompressor;
import org.bouncycastle.cms.jcajce.ZlibExpanderProvider;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.mail.smime.SMIMECompressed;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
import org.bouncycastle.mail.smime.SMIMECompressedParser;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Store;
public class SMIMECompressedTest
extends TestCase
{
private static final String COMPRESSED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7z\"; smime-type=compressed-data";
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
boolean DEBUG = true;
MimeBodyPart msg;
String signDN;
KeyPair signKP;
X509Certificate signCert;
String origDN;
KeyPair origKP;
X509Certificate origCert;
String reciDN;
KeyPair reciKP;
X509Certificate reciCert;
KeyPair dsaSignKP;
X509Certificate dsaSignCert;
KeyPair dsaOrigKP;
X509Certificate dsaOrigCert;
/*
*
* INFRASTRUCTURE
*
*/
public SMIMECompressedTest(
String name)
throws Exception
{
super(name);
msg = SMIMETestUtil.makeMimeBodyPart("Hello world!");
signDN = "O=Bouncy Castle, C=AU";
signKP = CMSTestUtil.makeKeyPair();
signCert = CMSTestUtil.makeCertificate(signKP, signDN, signKP, signDN);
origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
origKP = CMSTestUtil.makeKeyPair();
origCert = CMSTestUtil.makeCertificate(origKP, origDN, signKP, signDN);
}
public static void main(String args[])
{
junit.textui.TestRunner.run(SMIMECompressedTest.class);
}
public static Test suite()
{
return new SMIMETestSetup(new TestSuite(SMIMECompressedTest.class));
}
public void testHeaders()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
MimeBodyPart cbp = cgen.generate(msg, new ZlibCompressor());
assertEquals(COMPRESSED_CONTENT_TYPE, cbp.getHeader("Content-Type")[0]);
assertEquals("attachment; filename=\"smime.p7z\"", cbp.getHeader("Content-Disposition")[0]);
assertEquals("S/MIME Compressed Message", cbp.getHeader("Content-Description")[0]);
}
public void testBasic()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
MimeBodyPart cbp = cgen.generate(msg, new ZlibCompressor());
SMIMECompressed sc = new SMIMECompressed(cbp);
msg.writeTo(bOut);
assertTrue(Arrays.areEqual(bOut.toByteArray(), sc.getContent(new ZlibExpanderProvider())));
}
public void testParser()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
ByteArrayOutputStream bOut1 = new ByteArrayOutputStream();
ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
MimeBodyPart cbp = cgen.generate(msg, new ZlibCompressor());
SMIMECompressedParser sc = new SMIMECompressedParser(cbp);
msg.writeTo(bOut1);
InputStream in = sc.getContent(new ZlibExpanderProvider()).getContentStream();
int ch;
while ((ch = in.read()) >= 0)
{
bOut2.write(ch);
}
assertTrue(Arrays.areEqual(bOut1.toByteArray(), bOut2.toByteArray()));
}
/*
* test compressing and uncompressing of a multipart-signed message.
*/
public void testCompressedSHA1WithRSA()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smp = gen.generate(msg);
MimeMessage bp2 = new MimeMessage((Session)null);
bp2.setContent(smp);
bp2.saveChanges();
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
MimeBodyPart cbp = cgen.generate(bp2, new ZlibCompressor());
SMIMECompressed cm = new SMIMECompressed(cbp);
MimeMultipart mm = (MimeMultipart)SMIMEUtil.toMimeBodyPart(cm.getContent(new ZlibExpanderProvider())).getContent();
SMIMESigned s = new SMIMESigned(mm);
ByteArrayOutputStream _baos = new ByteArrayOutputStream();
msg.writeTo(_baos);
_baos.close();
byte[] _msgBytes = _baos.toByteArray();
_baos = new ByteArrayOutputStream();
s.getContent().writeTo(_baos);
_baos.close();
byte[] _resBytes = _baos.toByteArray();
assertEquals(true, Arrays.areEqual(_msgBytes, _resBytes));
certs = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
}
}

View File

@@ -0,0 +1,367 @@
package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.cms.jcajce.ZlibCompressor;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMESignedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.mail.smime.util.FileBackedMimeBodyPart;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public class SMIMEMiscTest
extends TestCase
{
static MimeBodyPart msg;
static String signDN;
static KeyPair signKP;
static X509Certificate signCert;
static String origDN;
static KeyPair origKP;
static X509Certificate origCert;
static String reciDN;
static KeyPair reciKP;
static X509Certificate reciCert;
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
KeyPair dsaSignKP;
X509Certificate dsaSignCert;
KeyPair dsaOrigKP;
X509Certificate dsaOrigCert;
static
{
try
{
msg = SMIMETestUtil.makeMimeBodyPart("Hello world!\n");
signDN = "O=Bouncy Castle, C=AU";
signKP = CMSTestUtil.makeKeyPair();
signCert = CMSTestUtil.makeCertificate(signKP, signDN, signKP, signDN);
origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
origKP = CMSTestUtil.makeKeyPair();
origCert = CMSTestUtil.makeCertificate(origKP, origDN, signKP, signDN);
}
catch (Exception e)
{
throw new RuntimeException("problem setting up signed test class: " + e);
}
}
/*
*
* INFRASTRUCTURE
*
*/
public SMIMEMiscTest(String name)
{
super(name);
}
public static void main(String args[])
{
Security.addProvider(new BouncyCastleProvider());
junit.textui.TestRunner.run(SMIMEMiscTest.class);
}
public static Test suite()
{
return new SMIMETestSetup(new TestSuite(SMIMEMiscTest.class));
}
public void testSHA256WithRSAParserEncryptedWithAES()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(origCert).setProvider("BC"));
MimeBodyPart mp = encGen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider("BC").build());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), (MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testSHA256WithRSACompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, new ZlibCompressor());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESigned s = new SMIMESigned((MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testQuotePrintableSigPreservation()
throws Exception
{
MimeMessage msg = new MimeMessage((Session)null, getClass().getResourceAsStream("qp-soft-break.eml"));
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(origCert).setProvider("BC"));
MimeBodyPart mp = encGen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider("BC").build());
SMIMEEnveloped env = new SMIMEEnveloped(mp);
RecipientInformation ri = (RecipientInformation)env.getRecipientInfos().getRecipients().iterator().next();
MimeBodyPart mm = SMIMEUtil.toMimeBodyPart(ri.getContentStream(new JceKeyTransEnvelopedRecipient(origKP.getPrivate()).setProvider("BC")));
SMIMESigned s = new SMIMESigned((MimeMultipart)mm.getContent());
Collection c = s.getSignerInfos().getSigners();
Iterator it = c.iterator();
Store certs = s.getCertificates();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
((FileBackedMimeBodyPart)mm).dispose();
}
public void testSHA256WithRSAParserCompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, new ZlibCompressor());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), (MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testBrokenEnvelope()
throws Exception
{
Session session = Session.getDefaultInstance(System.getProperties(), null);
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream("brokenEnv.message"));
try
{
new SMIMEEnveloped(msg);
}
catch (CMSException e)
{
if (!e.getMessage().equals("Malformed content."))
{
fail("wrong exception on bogus envelope");
}
}
}
private void verifySigners(Store certs, SignerInformationStore signers)
throws Exception
{
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
}
private void verifyMessageBytes(MimeBodyPart a, MimeBodyPart b)
throws Exception
{
ByteArrayOutputStream bOut1 = new ByteArrayOutputStream();
a.writeTo(bOut1);
bOut1.close();
ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
b.writeTo(bOut2);
bOut2.close();
assertEquals(true, Arrays.equals(bOut1.toByteArray(), bOut2.toByteArray()));
}
/**
* Create a mime message representing the multipart. We need to do
* this as otherwise no raw content stream for the message will exist.
*/
private MimeMessage createMimeMessage(File tmpFile, MimeMultipart smm)
throws Exception
{
FileOutputStream fOut = new FileOutputStream(tmpFile);
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(smm, smm.getContentType());
body.saveChanges();
body.writeTo(fOut);
fOut.close();
return new MimeMessage(session, new FileInputStream(tmpFile));
}
private ASN1EncodableVector generateSignedAttributes()
{
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
return signedAttrs;
}
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2005 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
package org.bouncycastle.mail.smime.test;
import junit.extensions.TestSetup;
import junit.framework.Test;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import java.security.Security;
class SMIMETestSetup extends TestSetup
{
private CommandMap originalMap = null;
public SMIMETestSetup(Test test)
{
super(test);
}
protected void setUp()
{
Security
.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
MailcapCommandMap _mailcap = (MailcapCommandMap)CommandMap
.getDefaultCommandMap();
_mailcap
.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
_mailcap
.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
_mailcap
.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
_mailcap
.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
_mailcap
.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
originalMap = CommandMap.getDefaultCommandMap();
CommandMap.setDefaultCommandMap(_mailcap);
}
protected void tearDown()
{
CommandMap.setDefaultCommandMap(originalMap);
originalMap = null;
Security.removeProvider("BC");
}
}

View File

@@ -0,0 +1,53 @@
package org.bouncycastle.mail.smime.test;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import java.security.Security;
public class SMIMETestUtil
{
public static final boolean DEBUG = true;
static
{
Security.addProvider(new BouncyCastleProvider());
}
/*
*
* MAIL
*
*/
public static MimeBodyPart makeMimeBodyPart(String msg)
throws MessagingException
{
MimeBodyPart _mbp = new MimeBodyPart();
_mbp.setText(msg);
return _mbp;
}
public static MimeBodyPart makeMimeBodyPart(MimeMultipart mm)
throws MessagingException
{
MimeBodyPart _mbp = new MimeBodyPart();
_mbp.setContent(mm, mm.getContentType());
return _mbp;
}
public static MimeMultipart makeMimeMultipart(String msg1, String msg2)
throws MessagingException
{
MimeMultipart _mm = new MimeMultipart();
_mm.addBodyPart(makeMimeBodyPart(msg1));
_mm.addBodyPart(makeMimeBodyPart(msg2));
return _mm;
}
}

View File

@@ -0,0 +1,474 @@
package org.bouncycastle.mail.smime.test;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.CertStore;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.i18n.ErrorBundle;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.validator.SignedMailValidator;
import org.bouncycastle.util.Store;
import org.bouncycastle.x509.PKIXCertPathReviewer;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
public class SignedMailValidatorTest extends TestCase
{
static String TEST_TRUST_ACHOR = "validator.root.crt";
public void testShortKey() throws Exception
{
String message = "validator.shortKey.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isValidSignature());
assertContainsMessage(result.getNotifications(),
"SignedMailValidator.shortSigningKey",
"Warning: The signing key is only 512 bits long.");
}
public void testKeyUsage() throws Exception
{
String message = "validator.keyUsage.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertTrue(result.getCertPathReview().isValidCertPath());
assertFalse(result.isValidSignature());
assertContainsMessage(
result.getErrors(),
"SignedMailValidator.signingNotPermitted",
"The key usage extension of signer certificate does not permit using the key for email signatures.");
}
public void testExtKeyUsage() throws Exception
{
String message = "validator.extKeyUsage.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertTrue(result.getCertPathReview().isValidCertPath());
assertFalse(result.isValidSignature());
assertContainsMessage(
result.getErrors(),
"SignedMailValidator.extKeyUsageNotPermitted",
"The extended key usage extension of the signer certificate does not permit using the key for email signatures.");
}
public void testNoEmail() throws Exception
{
String message = "validator.noEmail.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertTrue(result.getCertPathReview().isValidCertPath());
assertFalse(result.isValidSignature());
assertContainsMessage(
result.getErrors(),
"SignedMailValidator.noEmailInCert",
"The signer certificate is not usable for email signatures: it contains no email address.");
}
public void testNotYetValid() throws Exception
{
String message = "validator.notYetValid.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertFalse(result.isValidSignature());
assertContainsMessage(result.getErrors(),
"SignedMailValidator.certNotYetValid",
"The message was signed at Aug 28, 2006 3:04:01 PM GMT. But the certificate is not valid before Dec 28, 2006 2:19:31 PM GMT.");
PKIXCertPathReviewer review = result.getCertPathReview();
assertFalse(review.isValidCertPath());
assertContainsMessage(
review.getErrors(0),
"CertPathReviewer.certificateNotYetValid",
"Could not validate the certificate. Certificate is not valid until Dec 28, 2006 2:19:31 PM GMT.");
}
public void testExpired() throws Exception
{
String message = "validator.expired.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertFalse(result.isValidSignature());
assertContainsMessage(result.getErrors(),
"SignedMailValidator.certExpired",
"The message was signed at Sep 1, 2006 9:08:35 AM GMT. But the certificate expired at Sep 1, 2006 8:39:20 AM GMT.");
PKIXCertPathReviewer review = result.getCertPathReview();
assertFalse(review.isValidCertPath());
assertContainsMessage(
review.getErrors(0),
"CertPathReviewer.certificateExpired",
"Could not validate the certificate. Certificate expired on Sep 1, 2006 8:39:20 AM GMT.");
}
public void testRevoked() throws Exception
{
String message = "validator.revoked.eml";
PKIXParameters params = createDefaultParams();
List crlList = new ArrayList();
crlList.add(loadCRL("validator.revoked.crl"));
CertStore crls = CertStore.getInstance("Collection",new CollectionCertStoreParameters(crlList));
params.addCertStore(crls);
params.setRevocationEnabled(true);
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertFalse(result.isValidSignature());
PKIXCertPathReviewer review = result.getCertPathReview();
assertFalse(review.isValidCertPath());
assertContainsMessage(
review.getErrors(0),
"CertPathReviewer.certRevoked",
"The certificate was revoked at Sep 1, 2006 9:30:00 AM GMT. Reason: Key Compromise.");
}
public void testLongValidity() throws Exception
{
String message = "validator.longValidity.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertTrue(result.isValidSignature());
assertContainsMessage(result.getNotifications(),
"SignedMailValidator.longValidity",
"Warning: The signing certificate has a very long validity period: from Sep 1, 2006 11:00:00 AM GMT until Aug 8, 2106 11:00:00 AM GMT.");
}
public void testSelfSignedCert()
throws Exception
{
MimeBodyPart baseMsg = SMIMETestUtil.makeMimeBodyPart("Hello world!\n");
String signDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
KeyPair signKP = CMSTestUtil.makeKeyPair();
X509Certificate signCert = CMSTestUtil.makeV1Certificate(signKP, signDN, signKP, signDN);
// check basic path validation
Set trustanchors = new HashSet();
TrustAnchor ta = new TrustAnchor(signCert, null);
trustanchors.add(ta);
X509Certificate rootCert = ta.getTrustedCert();
// init cert stores
List certStores = new ArrayList();
List certList = new ArrayList();
certList.add(rootCert);
CertStore store = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList));
certStores.add(store);
// first path
CertPath path = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores);
assertTrue("path size is not 1", path.getCertificates().size() == 1);
// check message validation
certList = new ArrayList();
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").build("SHA1withRSA", signKP.getPrivate(), signCert));
gen.addCertificates(certs);
MimeMultipart signedMsg = gen.generate(baseMsg);
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// read message
MimeMessage msg = new MimeMessage(session);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
msg.setFrom(fromUser);
msg.setRecipient(Message.RecipientType.TO, toUser);
msg.setContent(signedMsg, signedMsg.getContentType());
msg.saveChanges();
PKIXParameters params = new PKIXParameters(trustanchors);
params.setRevocationEnabled(false);
SignedMailValidator validator = new SignedMailValidator(msg, params);
SignerInformation signer = (SignerInformation) validator
.getSignerInformationStore().getSigners().iterator().next();
SignedMailValidator.ValidationResult res = validator.getValidationResult(signer);
assertTrue(res.isVerifiedSignature());
assertTrue(res.isValidSignature());
}
// TODO: this test needs to be replaced, unfortunately it was working due to a bug in
// trust anchor extension handling
// public void testCorruptRootStore() throws Exception
// {
// String message = "validator.validMail.eml";
// Set trustanchors = new HashSet();
// trustanchors.add(getTrustAnchor(TEST_TRUST_ACHOR));
// trustanchors.add(getTrustAnchor("validator.fakeRoot.crt"));
// PKIXParameters params = new PKIXParameters(trustanchors);
// params.setRevocationEnabled(false);
//
// SignedMailValidator.ValidationResult result = doTest(message, params);
//
// assertTrue(result.isVerifiedSignature());
// assertFalse(result.isValidSignature());
//
// PKIXCertPathReviewer review = result.getCertPathReview();
//
// assertFalse(review.isValidCertPath());
// assertContainsMessage(review.getErrors(-1),
// "CertPathReviewer.conflictingTrustAnchors",
// "Warning: corrupt trust root store: There are 2 trusted public keys for the CA \"CN=SignedMailValidatorTest Root, C=CH\" - please ensure with CA which is the correct key.");
// }
public void testCircular() throws Exception
{
String message = "circular.eml";
PKIXParameters params = createDefaultParams();
SignedMailValidator.ValidationResult result = doTest(message, params);
assertTrue(result.isVerifiedSignature());
assertFalse(result.isValidSignature());
assertFalse(result.getCertPathReview().isValidCertPath());
assertTrue("cert path size", result.getCertPathReview().getCertPathSize() > 2);
}
public void testExtendedReviewer() throws Exception
{
try
{
// Get a Session object with the default properties.
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// read message
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream("validator.shortKey.eml"));
SignedMailValidator validator = new SignedMailValidator(msg, createDefaultParams(), String.class);
fail();
}
catch (IllegalArgumentException e)
{
assertTrue(e.getMessage().startsWith("certPathReviewerClass is not a subclass of"));
}
// Get a Session object with the default properties.
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// read message
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream("validator.shortKey.eml"));
SignedMailValidator validator = new SignedMailValidator(msg, createDefaultParams(), DummyCertPathReviewer.class);
SignerInformation sInfo = (SignerInformation) validator.getSignerInformationStore().getSigners().iterator().next();
SignedMailValidator.ValidationResult result = validator.getValidationResult(sInfo);
assertTrue(result.isValidSignature());
assertContainsMessage(result.getNotifications(),
"SignedMailValidator.shortSigningKey",
"Warning: The signing key is only 512 bits long.");
}
public void testCreateCertPath() throws Exception
{
// load trust anchor
Set trustanchors = new HashSet();
TrustAnchor ta = getTrustAnchor("certpath_root.crt");
trustanchors.add(ta);
X509Certificate rootCert = ta.getTrustedCert();
X509Certificate interCert1 = loadCert("certpath_inter1.crt");
X509Certificate interCert2 = loadCert("certpath_inter2.crt");
X509Certificate endCert1 = loadCert("certpath_end1.crt");
X509Certificate endCert2 = loadCert("certpath_end2.crt");
// init cert stores
List certStores = new ArrayList();
List certList = new ArrayList();
certList.add(interCert1);
certList.add(interCert2);
CertStore store = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList));
certStores.add(store);
// first path
CertPath path = SignedMailValidator.createCertPath(endCert1, trustanchors, certStores);
assertTrue("path size is not 3", path.getCertificates().size() == 3);
assertEquals("different end certificate", path.getCertificates().get(0), endCert1);
assertEquals("different intermediate certificate", path.getCertificates().get(1), interCert1);
assertEquals("different root certificate", path.getCertificates().get(2), rootCert);
// second path
path = SignedMailValidator.createCertPath(endCert2, trustanchors, certStores);
assertTrue("path size is not 3", path.getCertificates().size() == 3);
assertEquals("different end certificate", path.getCertificates().get(0), endCert2);
assertEquals("different intermediate certificate", path.getCertificates().get(1), interCert2);
assertEquals("different root certificate", path.getCertificates().get(2), rootCert);
}
private SignedMailValidator.ValidationResult doTest(String message,
PKIXParameters params) throws Exception
{
// Get a Session object with the default properties.
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// read message
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream(message));
SignedMailValidator validator = new SignedMailValidator(msg, params);
SignerInformation signer = (SignerInformation) validator
.getSignerInformationStore().getSigners().iterator().next();
return validator.getValidationResult(signer);
}
private void assertContainsMessage(List msgList, String messageId,
String text) throws Exception
{
Iterator it = msgList.iterator();
boolean found = false;
while (it.hasNext())
{
ErrorBundle message = (ErrorBundle) it.next();
if (message.getId().equals(messageId))
{
found = true;
assertEquals(text, message.getText(Locale.ENGLISH, TimeZone
.getTimeZone("GMT")));
break;
}
}
assertTrue("Expected message not found!", found);
}
private PKIXParameters createDefaultParams() throws Exception
{
Set trustanchors = new HashSet();
trustanchors.add(getTrustAnchor(TEST_TRUST_ACHOR));
PKIXParameters defParams = new PKIXParameters(trustanchors);
defParams.setRevocationEnabled(false);
return defParams;
}
private TrustAnchor getTrustAnchor(String trustcert) throws Exception
{
X509Certificate cert = loadCert(trustcert);
if (cert != null)
{
byte[] ncBytes = cert
.getExtensionValue(X509Extension.nameConstraints.getId());
if (ncBytes != null)
{
ASN1Encodable extValue = X509ExtensionUtil
.fromExtensionValue(ncBytes);
return new TrustAnchor(cert, extValue.toASN1Primitive().getEncoded(ASN1Encoding.DER));
}
return new TrustAnchor(cert, null);
}
return null;
}
private X509Certificate loadCert(String certfile) throws Exception
{
X509Certificate cert = null;
InputStream in = getClass().getResourceAsStream(certfile);
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
cert = (X509Certificate) cf.generateCertificate(in);
return cert;
}
private X509CRL loadCRL(String crlfile) throws Exception
{
X509CRL crl = null;
InputStream in = this.getClass().getResourceAsStream(crlfile);
CertificateFactory cf = CertificateFactory.getInstance("x.509", "BC");
crl = (X509CRL) cf.generateCRL(in);
return crl;
}
public void setUp()
{
if (Security.getProvider("BC") == null)
{
Security
.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
}
public static void main(String[] args) throws Exception
{
junit.textui.TestRunner.run(suite());
}
public static Test suite() throws Exception
{
TestSuite suite = new TestSuite("SignedMailValidator Tests");
suite.addTestSuite(SignedMailValidatorTest.class);
return suite;
}
}

View File

@@ -0,0 +1,224 @@
package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyPair;
import org.bouncycastle.jce.cert.CertStore;
import org.bouncycastle.jce.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.mail.smime.SMIMECompressed;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
import org.bouncycastle.mail.smime.SMIMECompressedParser;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.util.Arrays;
public class SMIMECompressedTest
extends TestCase
{
private static final String COMPRESSED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7z\"; smime-type=compressed-data";
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
boolean DEBUG = true;
MimeBodyPart msg;
String signDN;
KeyPair signKP;
X509Certificate signCert;
String origDN;
KeyPair origKP;
X509Certificate origCert;
String reciDN;
KeyPair reciKP;
X509Certificate reciCert;
KeyPair dsaSignKP;
X509Certificate dsaSignCert;
KeyPair dsaOrigKP;
X509Certificate dsaOrigCert;
/*
*
* INFRASTRUCTURE
*
*/
public SMIMECompressedTest(
String name)
throws Exception
{
super(name);
msg = SMIMETestUtil.makeMimeBodyPart("Hello world!");
signDN = "O=Bouncy Castle, C=AU";
signKP = CMSTestUtil.makeKeyPair();
signCert = CMSTestUtil.makeCertificate(signKP, signDN, signKP, signDN);
origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
origKP = CMSTestUtil.makeKeyPair();
origCert = CMSTestUtil.makeCertificate(origKP, origDN, signKP, signDN);
}
public static void main(String args[])
{
junit.textui.TestRunner.run(SMIMECompressedTest.class);
}
public static Test suite()
{
return new SMIMETestSetup(new TestSuite(SMIMECompressedTest.class));
}
public void testHeaders()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
MimeBodyPart cbp = cgen.generate(msg, SMIMECompressedGenerator.ZLIB);
assertEquals(COMPRESSED_CONTENT_TYPE, cbp.getHeader("Content-Type")[0]);
assertEquals("attachment; filename=\"smime.p7z\"", cbp.getHeader("Content-Disposition")[0]);
assertEquals("S/MIME Compressed Message", cbp.getHeader("Content-Description")[0]);
}
public void testBasic()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
MimeBodyPart cbp = cgen.generate(msg, SMIMECompressedGenerator.ZLIB);
SMIMECompressed sc = new SMIMECompressed(cbp);
msg.writeTo(bOut);
assertTrue(Arrays.areEqual(bOut.toByteArray(), sc.getContent()));
}
public void testParser()
throws Exception
{
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
ByteArrayOutputStream bOut1 = new ByteArrayOutputStream();
ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
MimeBodyPart cbp = cgen.generate(msg, SMIMECompressedGenerator.ZLIB);
SMIMECompressedParser sc = new SMIMECompressedParser(cbp);
msg.writeTo(bOut1);
InputStream in = sc.getContent().getContentStream();
int ch;
while ((ch = in.read()) >= 0)
{
bOut2.write(ch);
}
assertTrue(Arrays.areEqual(bOut1.toByteArray(), bOut2.toByteArray()));
}
/*
* test compressing and uncompressing of a multipart-signed message.
*/
public void testCompressedSHA1WithRSA()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
CertStore certs = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList), "BC");
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSigner(origKP.getPrivate(), origCert, SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(signedAttrs), null);
gen.addCertificatesAndCRLs(certs);
MimeMultipart smp = gen.generate(msg, "BC");
MimeMessage bp2 = new MimeMessage((Session)null);
bp2.setContent(smp);
bp2.saveChanges();
SMIMECompressedGenerator cgen = new SMIMECompressedGenerator();
MimeBodyPart cbp = cgen.generate(bp2, SMIMECompressedGenerator.ZLIB);
SMIMECompressed cm = new SMIMECompressed(cbp);
MimeMultipart mm = (MimeMultipart)SMIMEUtil.toMimeBodyPart(cm.getContent()).getContent();
SMIMESigned s = new SMIMESigned(mm);
ByteArrayOutputStream _baos = new ByteArrayOutputStream();
msg.writeTo(_baos);
_baos.close();
byte[] _msgBytes = _baos.toByteArray();
_baos = new ByteArrayOutputStream();
s.getContent().writeTo(_baos);
_baos.close();
byte[] _resBytes = _baos.toByteArray();
assertEquals(true, Arrays.areEqual(_msgBytes, _resBytes));
certs = s.getCertificatesAndCRLs("Collection", "BC");
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate)certIt.next();
assertEquals(true, signer.verify(cert, "BC"));
}
}
}

View File

@@ -0,0 +1,361 @@
package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.Security;
import org.bouncycastle.jce.cert.CertStore;
import org.bouncycastle.jce.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.cms.test.CMSTestUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMESignedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.mail.smime.util.FileBackedMimeBodyPart;
public class SMIMEMiscTest
extends TestCase
{
static MimeBodyPart msg;
static String signDN;
static KeyPair signKP;
static X509Certificate signCert;
static String origDN;
static KeyPair origKP;
static X509Certificate origCert;
static String reciDN;
static KeyPair reciKP;
static X509Certificate reciCert;
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
KeyPair dsaSignKP;
X509Certificate dsaSignCert;
KeyPair dsaOrigKP;
X509Certificate dsaOrigCert;
static
{
try
{
msg = SMIMETestUtil.makeMimeBodyPart("Hello world!\n");
signDN = "O=Bouncy Castle, C=AU";
signKP = CMSTestUtil.makeKeyPair();
signCert = CMSTestUtil.makeCertificate(signKP, signDN, signKP, signDN);
origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
origKP = CMSTestUtil.makeKeyPair();
origCert = CMSTestUtil.makeCertificate(origKP, origDN, signKP, signDN);
}
catch (Exception e)
{
throw new RuntimeException("problem setting up signed test class: " + e);
}
}
/*
*
* INFRASTRUCTURE
*
*/
public SMIMEMiscTest(String name)
{
super(name);
}
public static void main(String args[])
{
Security.addProvider(new BouncyCastleProvider());
junit.textui.TestRunner.run(SMIMEMiscTest.class);
}
public static Test suite()
{
return new SMIMETestSetup(new TestSuite(SMIMEMiscTest.class));
}
public void testSHA256WithRSAParserEncryptedWithAES()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
CertStore certs = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList), "BC");
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addKeyTransRecipient(origCert);
MimeBodyPart mp = encGen.generate(msg, SMIMEEnvelopedGenerator.AES128_CBC, "BC");
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSigner(origKP.getPrivate(), origCert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null);
gen.addCertificatesAndCRLs(certs);
MimeMultipart smm = gen.generate(mp, "BC");
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser((MimeMultipart)msg.getContent());
certs = s.getCertificatesAndCRLs("Collection", "BC");
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testSHA256WithRSACompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
CertStore certs = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList), "BC");
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, SMIMECompressedGenerator.ZLIB);
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSigner(origKP.getPrivate(), origCert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null);
gen.addCertificatesAndCRLs(certs);
MimeMultipart smm = gen.generate(mp, "BC");
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESigned s = new SMIMESigned((MimeMultipart)msg.getContent());
certs = s.getCertificatesAndCRLs("Collection", "BC");
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testQuotePrintableSigPreservation()
throws Exception
{
MimeMessage msg = new MimeMessage((Session)null, getClass().getResourceAsStream("qp-soft-break.eml"));
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addKeyTransRecipient(origCert);
MimeBodyPart mp = encGen.generate(msg, SMIMEEnvelopedGenerator.AES128_CBC, "BC");
SMIMEEnveloped env = new SMIMEEnveloped(mp);
RecipientInformation ri = (RecipientInformation)env.getRecipientInfos().getRecipients().iterator().next();
MimeBodyPart mm = SMIMEUtil.toMimeBodyPart(ri.getContentStream(origKP.getPrivate(), "BC"));
SMIMESigned s = new SMIMESigned((MimeMultipart)mm.getContent());
Collection c = s.getSignerInfos().getSigners();
Iterator it = c.iterator();
CertStore certs = s.getCertificatesAndCRLs("Collection", "BC");
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate)certIt.next();
assertEquals(true, signer.verify(cert, "BC"));
}
((FileBackedMimeBodyPart)mm).dispose();
}
public void testSHA256WithRSAParserCompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
CertStore certs = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList), "BC");
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, SMIMECompressedGenerator.ZLIB);
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSigner(origKP.getPrivate(), origCert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null);
gen.addCertificatesAndCRLs(certs);
MimeMultipart smm = gen.generate(mp, "BC");
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser((MimeMultipart)msg.getContent());
certs = s.getCertificatesAndCRLs("Collection", "BC");
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testBrokenEnvelope()
throws Exception
{
Session session = Session.getDefaultInstance(System.getProperties(), null);
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream("brokenEnv.message"));
try
{
new SMIMEEnveloped(msg);
}
catch (CMSException e)
{
if (!e.getMessage().equals("Malformed content."))
{
fail("wrong exception on bogus envelope");
}
}
}
private void verifySigners(CertStore certs, SignerInformationStore signers)
throws Exception
{
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate)certIt.next();
assertEquals(true, signer.verify(cert, "BC"));
}
}
private void verifyMessageBytes(MimeBodyPart a, MimeBodyPart b)
throws Exception
{
ByteArrayOutputStream bOut1 = new ByteArrayOutputStream();
a.writeTo(bOut1);
bOut1.close();
ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
b.writeTo(bOut2);
bOut2.close();
assertEquals(true, Arrays.equals(bOut1.toByteArray(), bOut2.toByteArray()));
}
/**
* Create a mime message representing the multipart. We need to do
* this as otherwise no raw content stream for the message will exist.
*/
private MimeMessage createMimeMessage(File tmpFile, MimeMultipart smm)
throws Exception
{
FileOutputStream fOut = new FileOutputStream(tmpFile);
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(smm, smm.getContentType());
body.saveChanges();
body.writeTo(fOut);
fOut.close();
return new MimeMessage(session, new FileInputStream(tmpFile));
}
private ASN1EncodableVector generateSignedAttributes()
{
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
return signedAttrs;
}
}

View File

@@ -0,0 +1,25 @@
package org.bouncycastle.mail.smime.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class AllTests
{
public static void main (String[] args)
throws Exception
{
junit.textui.TestRunner.run (suite());
}
public static Test suite()
throws Exception
{
TestSuite suite= new TestSuite("SMIME tests");
suite.addTest(NewSMIMESignedTest.suite());
suite.addTest(NewSMIMEEnvelopedTest.suite());
suite.addTest(SMIMECompressedTest.suite());
suite.addTest(SMIMEMiscTest.suite());
return suite;
}
}