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 bcjmail-fips Show documentation
Show all versions of bcjmail-fips Show documentation
The Bouncy Castle Java APIs for doing S/MIME with the Jakarta Mail APIs. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.
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 jakarta.mail.MessagingException;
import jakarta.mail.Part;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.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);
}
}
}