org.bouncycastle.mail.smime.SMIMESignedParser Maven / Gradle / Ivy
Show all versions of bcmail-jdk14 Show documentation
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 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.
*
* 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", "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++;
* }
* }
*
* * 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 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), SMIMEUtil.getInputStreamNoMultipartSigned(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, SMIMEUtil.getInputStreamNoMultipartSigned(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, SMIMEUtil.getInputStreamNoMultipartSigned(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(); } } }