001: package org.bouncycastle.mail.smime;
002:
003: import org.bouncycastle.cms.CMSException;
004: import org.bouncycastle.cms.CMSProcessable;
005: import org.bouncycastle.cms.CMSSignedData;
006:
007: import javax.activation.CommandMap;
008: import javax.activation.MailcapCommandMap;
009: import javax.mail.MessagingException;
010: import javax.mail.Part;
011: import javax.mail.Session;
012: import javax.mail.internet.MimeBodyPart;
013: import javax.mail.internet.MimeMessage;
014: import javax.mail.internet.MimeMultipart;
015: import javax.mail.internet.MimePart;
016: import java.io.ByteArrayInputStream;
017: import java.io.ByteArrayOutputStream;
018: import java.io.IOException;
019: import java.io.InputStream;
020:
021: /**
022: * general class for handling a pkcs7-signature message.
023: * <p>
024: * A simple example of usage - note, in the example below the validity of
025: * the certificate isn't verified, just the fact that one of the certs
026: * matches the given signer...
027: * <p>
028: * <pre>
029: * CertStore certs = s.getCertificates("Collection", "BC");
030: * SignerInformationStore signers = s.getSignerInfos();
031: * Collection c = signers.getSigners();
032: * Iterator it = c.iterator();
033: *
034: * while (it.hasNext())
035: * {
036: * SignerInformation signer = (SignerInformation)it.next();
037: * Collection certCollection = certs.getCertificates(signer.getSID());
038: *
039: * Iterator certIt = certCollection.iterator();
040: * X509Certificate cert = (X509Certificate)certIt.next();
041: *
042: * if (signer.verify(cert.getPublicKey()))
043: * {
044: * verified++;
045: * }
046: * }
047: * </pre>
048: * <p>
049: * Note: if you are using this class with AS2 or some other protocol
050: * that does not use 7bit as the default content transfer encoding you
051: * will need to use the constructor that allows you to specify the default
052: * content transfer encoding, such as "binary".
053: * </p>
054: */
055: public class SMIMESigned extends CMSSignedData {
056: Object message;
057: MimeBodyPart content;
058:
059: private static InputStream getInputStream(Part bodyPart)
060: throws MessagingException {
061: try {
062: if (bodyPart.isMimeType("multipart/signed")) {
063: throw new MessagingException(
064: "attempt to create signed data object from multipart content - use MimeMultipart constructor.");
065: }
066:
067: return bodyPart.getInputStream();
068: } catch (IOException e) {
069: throw new MessagingException("can't extract input stream: "
070: + e);
071: }
072: }
073:
074: static {
075: MailcapCommandMap mc = (MailcapCommandMap) CommandMap
076: .getDefaultCommandMap();
077:
078: mc
079: .addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
080: mc
081: .addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
082: mc
083: .addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
084: mc
085: .addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
086: mc
087: .addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
088:
089: CommandMap.setDefaultCommandMap(mc);
090: }
091:
092: /**
093: * base constructor using a defaultContentTransferEncoding of 7bit
094: *
095: * @exception MessagingException on an error extracting the signature or
096: * otherwise processing the message.
097: * @exception CMSException if some other problem occurs.
098: */
099: public SMIMESigned(MimeMultipart message)
100: throws MessagingException, CMSException {
101: super (
102: new CMSProcessableBodyPartInbound(message
103: .getBodyPart(0)), getInputStream(message
104: .getBodyPart(1)));
105:
106: this .message = message;
107: this .content = (MimeBodyPart) message.getBodyPart(0);
108: }
109:
110: /**
111: * base constructor with settable contentTransferEncoding
112: *
113: * @param message the signed message
114: * @param defaultContentTransferEncoding new default to use
115: * @exception MessagingException on an error extracting the signature or
116: * otherwise processing the message.
117: * @exception CMSException if some other problem occurs.
118: */
119: public SMIMESigned(MimeMultipart message,
120: String defaultContentTransferEncoding)
121: throws MessagingException, CMSException {
122: super (new CMSProcessableBodyPartInbound(message.getBodyPart(0),
123: defaultContentTransferEncoding), getInputStream(message
124: .getBodyPart(1)));
125:
126: this .message = message;
127: this .content = (MimeBodyPart) message.getBodyPart(0);
128: }
129:
130: /**
131: * base constructor for a signed message with encapsulated content.
132: *
133: * @exception MessagingException on an error extracting the signature or
134: * otherwise processing the message.
135: * @exception SMIMEException if the body part encapsulated in the message cannot be extracted.
136: * @exception CMSException if some other problem occurs.
137: */
138: public SMIMESigned(Part message) throws MessagingException,
139: CMSException, SMIMEException {
140: super (getInputStream(message));
141:
142: this .message = message;
143:
144: CMSProcessable cont = this .getSignedContent();
145:
146: if (cont != null) {
147: byte[] contBytes = (byte[]) cont.getContent();
148:
149: this .content = SMIMEUtil.toMimeBodyPart(contBytes);
150: }
151: }
152:
153: /**
154: * return the content that was signed.
155: */
156: public MimeBodyPart getContent() {
157: return content;
158: }
159:
160: /**
161: * Return the content that was signed as a mime message.
162: *
163: * @param session
164: * @return a MimeMessage holding the content.
165: * @throws MessagingException
166: */
167: public MimeMessage getContentAsMimeMessage(Session session)
168: throws MessagingException, IOException {
169: Object content = getSignedContent().getContent();
170: byte[] contentBytes = null;
171:
172: if (content instanceof byte[]) {
173: contentBytes = (byte[]) content;
174: } else if (content instanceof MimePart) {
175: MimePart part = (MimePart) content;
176: ByteArrayOutputStream out;
177:
178: if (part.getSize() > 0) {
179: out = new ByteArrayOutputStream(part.getSize());
180: } else {
181: out = new ByteArrayOutputStream();
182: }
183:
184: part.writeTo(out);
185: contentBytes = out.toByteArray();
186: } else {
187: String type = "<null>";
188: if (content != null) {
189: type = content.getClass().getName();
190: }
191:
192: throw new MessagingException(
193: "Could not transfrom content of type " + type
194: + " into MimeMessage.");
195: }
196:
197: if (contentBytes != null) {
198: ByteArrayInputStream in = new ByteArrayInputStream(
199: contentBytes);
200:
201: return new MimeMessage(session, in);
202: }
203:
204: return null;
205: }
206:
207: /**
208: * return the content that was signed - depending on whether this was
209: * unencapsulated or not it will return a MimeMultipart or a MimeBodyPart
210: */
211: public Object getContentWithSignature() {
212: return message;
213: }
214: }
|