org.bouncycastle.mail.smime.SMIMESignedParser 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.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 java.security.AccessController;
import java.security.PrivilegedAction;
import jakarta.activation.CommandMap;
import jakarta.activation.MailcapCommandMap;
import jakarta.mail.BodyPart;
import jakarta.mail.MessagingException;
import jakarta.mail.Part;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.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.
*
* 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...
*
*
* CertStore certs = s.getCertificates("Collection", "BCFIPS");
* 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++;
* }
* }
*
*
* 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".
*
*/
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, true, 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
{
CommandMap commandMap = CommandMap.getDefaultCommandMap();
if (commandMap instanceof MailcapCommandMap)
{
final MailcapCommandMap mc = (MailcapCommandMap)commandMap;
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");
AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
CommandMap.setDefaultCommandMap(mc);
return null;
}
});
}
}
/**
* 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.
*
* 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.
*
* @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();
}
}
}