org.bouncycastle.mail.smime.SMIMEToolkit Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail-jdk15on Show documentation
Show all versions of bcmail-jdk15on Show documentation
The Bouncy Castle Java S/MIME APIs for handling S/MIME protocols. This jar contains S/MIME APIs for JDK 1.5 to JDK 1.8. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs. The JavaMail API and the Java activation framework will also be needed.
package org.bouncycastle.mail.smime;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.Recipient;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInfoGenerator;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.util.CollectionStore;
/**
* A tool kit of common tasks.
*/
public class SMIMEToolkit
{
private final DigestCalculatorProvider digestCalculatorProvider;
/**
* Base constructor.
*
* @param digestCalculatorProvider provider for any digest calculations required.
*/
public SMIMEToolkit(DigestCalculatorProvider digestCalculatorProvider)
{
this.digestCalculatorProvider = digestCalculatorProvider;
}
/**
* Return true if the passed in message (MimeBodyPart or MimeMessage) is encrypted.
*
* @param message message of interest
* @return true if the message represents an encrypted message, false otherwise.
* @throws MessagingException on a message processing issue.
*/
public boolean isEncrypted(Part message)
throws MessagingException
{
return message.getHeader("Content-Type")[0].equals("application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data");
}
/**
* Return true if the passed in message (MimeBodyPart or MimeMessage) is a signed one.
*
* @param message message of interest
* @return true if the message represents a signed message, false otherwise.
* @throws MessagingException on a message processing issue.
*/
public boolean isSigned(Part message)
throws MessagingException
{
return message.getHeader("Content-Type")[0].startsWith("multipart/signed")
|| message.getHeader("Content-Type")[0].equals("application/pkcs7-mime; name=smime.p7m; smime-type=signed-data");
}
/**
* Return true if the passed in MimeMultipart is a signed one.
*
* @param message message of interest
* @return true if the multipart has an attached signature, false otherwise.
* @throws MessagingException on a message processing issue.
*/
public boolean isSigned(MimeMultipart message)
throws MessagingException
{
return message.getBodyPart(1).getHeader("Content-Type")[0].equals("application/pkcs7-signature; name=smime.p7s; smime-type=signed-data");
}
/**
* Return true if there is a signature on the message that can be verified by the verifier.
*
* @param message a MIME part representing a signed message.
* @param verifier the verifier we want to find a signer for.
* @return true if cert verifies message, false otherwise.
* @throws SMIMEException on a SMIME handling issue.
* @throws MessagingException on a basic message processing exception
*/
public boolean isValidSignature(Part message, SignerInformationVerifier verifier)
throws SMIMEException, MessagingException
{
try
{
SMIMESignedParser s;
if (message.isMimeType("multipart/signed"))
{
s = new SMIMESignedParser(digestCalculatorProvider, (MimeMultipart)message.getContent());
}
else
{
s = new SMIMESignedParser(digestCalculatorProvider, message);
}
return isAtLeastOneValidSigner(s, verifier);
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new SMIMEException("Parsing failure: " + e.getMessage(), e);
}
}
private boolean isAtLeastOneValidSigner(SMIMESignedParser s, SignerInformationVerifier verifier)
throws CMSException
{
if (verifier.hasAssociatedCertificate())
{
X509CertificateHolder cert = verifier.getAssociatedCertificate();
SignerInformation signer = s.getSignerInfos().get(new SignerId(cert.getIssuer(), cert.getSerialNumber()));
if (signer != null)
{
return signer.verify(verifier);
}
}
Collection c = s.getSignerInfos().getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
if (signer.verify(verifier))
{
return true;
}
}
return false;
}
/**
* Return true if there is a signature on the message that can be verified by verifier..
*
* @param message a MIME part representing a signed message.
* @param verifier the verifier we want to find a signer for.
* @return true if cert verifies message, false otherwise.
* @throws SMIMEException on a SMIME handling issue.
* @throws MessagingException on a basic message processing exception
*/
public boolean isValidSignature(MimeMultipart message, SignerInformationVerifier verifier)
throws SMIMEException, MessagingException
{
try
{
SMIMESignedParser s = new SMIMESignedParser(digestCalculatorProvider, message);
return isAtLeastOneValidSigner(s, verifier);
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
}
/**
* Extract the signer's signing certificate from the message.
*
* @param message a MIME part/MIME message representing a signed message.
* @param signerInformation the signer information identifying the signer of interest.
* @return the signing certificate, null if not found.
*/
public X509CertificateHolder extractCertificate(Part message, SignerInformation signerInformation)
throws SMIMEException, MessagingException
{
try
{
SMIMESignedParser s;
if (message instanceof MimeMessage && message.isMimeType("multipart/signed"))
{
s = new SMIMESignedParser(digestCalculatorProvider, (MimeMultipart)message.getContent());
}
else
{
s = new SMIMESignedParser(digestCalculatorProvider, message);
}
Collection certCollection = s.getCertificates().getMatches(signerInformation.getSID());
Iterator certIt = certCollection.iterator();
if (certIt.hasNext())
{
return (X509CertificateHolder)certIt.next();
}
return null;
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new SMIMEException("Parsing failure: " + e.getMessage(), e);
}
}
/**
* Extract the signer's signing certificate from Multipart message content.
*
* @param message a MIME Multipart part representing a signed message.
* @param signerInformation the signer information identifying the signer of interest.
* @return the signing certificate, null if not found.
*/
public X509CertificateHolder extractCertificate(MimeMultipart message, SignerInformation signerInformation)
throws SMIMEException, MessagingException
{
try
{
SMIMESignedParser s = new SMIMESignedParser(digestCalculatorProvider, message);
Collection certCollection = s.getCertificates().getMatches(signerInformation.getSID());
Iterator certIt = certCollection.iterator();
if (certIt.hasNext())
{
return (X509CertificateHolder)certIt.next();
}
return null;
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
}
/**
* Produce a signed message in multi-part format with the second part containing a detached signature for the first.
*
* @param message the message to be signed.
* @param signerInfoGenerator the generator to be used to generate the signature.
* @return the resulting MimeMultipart
* @throws SMIMEException on an exception calculating or creating the signed data.
*/
public MimeMultipart sign(MimeBodyPart message, SignerInfoGenerator signerInfoGenerator)
throws SMIMEException
{
SMIMESignedGenerator gen = new SMIMESignedGenerator();
if (signerInfoGenerator.hasAssociatedCertificate())
{
List certList = new ArrayList();
certList.add(signerInfoGenerator.getAssociatedCertificate());
gen.addCertificates(new CollectionStore(certList));
}
gen.addSignerInfoGenerator(signerInfoGenerator);
return gen.generate(message);
}
/**
* Produce a signed message in encapsulated format where the message is encoded in the signature..
*
* @param message the message to be signed.
* @param signerInfoGenerator the generator to be used to generate the signature.
* @return a BodyPart containing the encapsulated message.
* @throws SMIMEException on an exception calculating or creating the signed data.
*/
public MimeBodyPart signEncapsulated(MimeBodyPart message, SignerInfoGenerator signerInfoGenerator)
throws SMIMEException
{
SMIMESignedGenerator gen = new SMIMESignedGenerator();
if (signerInfoGenerator.hasAssociatedCertificate())
{
List certList = new ArrayList();
certList.add(signerInfoGenerator.getAssociatedCertificate());
gen.addCertificates(new CollectionStore(certList));
}
gen.addSignerInfoGenerator(signerInfoGenerator);
return gen.generateEncapsulated(message);
}
/**
* Encrypt the passed in MIME part returning a new encrypted MIME part.
*
* @param mimePart the part to be encrypted.
* @param contentEncryptor the encryptor to use for the actual message content.
* @param recipientGenerator the generator for the target recipient.
* @return an encrypted MIME part.
* @throws SMIMEException in the event of an exception creating the encrypted part.
*/
public MimeBodyPart encrypt(MimeBodyPart mimePart, OutputEncryptor contentEncryptor, RecipientInfoGenerator recipientGenerator)
throws SMIMEException
{
SMIMEEnvelopedGenerator envGen = new SMIMEEnvelopedGenerator();
envGen.addRecipientInfoGenerator(recipientGenerator);
return envGen.generate(mimePart, contentEncryptor);
}
/**
* Encrypt the passed in MIME multi-part returning a new encrypted MIME part.
*
* @param multiPart the multi-part to be encrypted.
* @param contentEncryptor the encryptor to use for the actual message content.
* @param recipientGenerator the generator for the target recipient.
* @return an encrypted MIME part.
* @throws SMIMEException in the event of an exception creating the encrypted part.
*/
public MimeBodyPart encrypt(MimeMultipart multiPart, OutputEncryptor contentEncryptor, RecipientInfoGenerator recipientGenerator)
throws SMIMEException, MessagingException
{
SMIMEEnvelopedGenerator envGen = new SMIMEEnvelopedGenerator();
envGen.addRecipientInfoGenerator(recipientGenerator);
MimeBodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(multiPart);
return envGen.generate(bodyPart, contentEncryptor);
}
/**
* Encrypt the passed in MIME message returning a new encrypted MIME part.
*
* @param message the multi-part to be encrypted.
* @param contentEncryptor the encryptor to use for the actual message content.
* @param recipientGenerator the generator for the target recipient.
* @return an encrypted MIME part.
* @throws SMIMEException in the event of an exception creating the encrypted part.
*/
public MimeBodyPart encrypt(MimeMessage message, OutputEncryptor contentEncryptor, RecipientInfoGenerator recipientGenerator)
throws SMIMEException
{
SMIMEEnvelopedGenerator envGen = new SMIMEEnvelopedGenerator();
envGen.addRecipientInfoGenerator(recipientGenerator);
return envGen.generate(message, contentEncryptor);
}
/**
* Decrypt the passed in MIME part returning a part representing the decrypted content.
*
* @param mimePart the part containing the encrypted data.
* @param recipientId the recipient id in the date to be matched.
* @param recipient the recipient to be used if a match is found.
* @return a MIME part containing the decrypted content or null if the recipientId cannot be matched.
* @throws SMIMEException on an exception doing the decryption.
* @throws MessagingException on an exception parsing the message,
*/
public MimeBodyPart decrypt(MimeBodyPart mimePart, RecipientId recipientId, Recipient recipient)
throws SMIMEException, MessagingException
{
try
{
SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(mimePart);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipientInformation = recipients.get(recipientId);
if (recipientInformation == null)
{
return null;
}
return SMIMEUtil.toMimeBodyPart(recipientInformation.getContent(recipient));
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new SMIMEException("Parsing failure: " + e.getMessage(), e);
}
}
/**
* Decrypt the passed in MIME message returning a part representing the decrypted content.
*
* @param message the message containing the encrypted data.
* @param recipientId the recipient id in the date to be matched.
* @param recipient the recipient to be used if a match is found.
* @return a MIME part containing the decrypted content, or null if the recipientId cannot be matched.
* @throws SMIMEException on an exception doing the decryption.
* @throws MessagingException on an exception parsing the message,
*/
public MimeBodyPart decrypt(MimeMessage message, RecipientId recipientId, Recipient recipient)
throws SMIMEException, MessagingException
{
try
{
SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(message);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipientInformation = recipients.get(recipientId);
if (recipientInformation == null)
{
return null;
}
return SMIMEUtil.toMimeBodyPart(recipientInformation.getContent(recipient));
}
catch (CMSException e)
{
throw new SMIMEException("CMS processing failure: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new SMIMEException("Parsing failure: " + e.getMessage(), e);
}
}
}