001: /*
002: * Title: Oyster Project
003: * Description: S/MIME email sending capabilities
004: * @Author Vladan Obradovic, Vladimir Radisic
005: * @Version 2.1.6
006: */
007:
008: package org.enhydra.oyster.smime;
009:
010: import org.enhydra.oyster.exception.SMIMEException;
011: import org.enhydra.oyster.activation.CMSEnvelopedDataSource;
012: import org.enhydra.oyster.crypto.consts.EnvelopedConstants;
013: import javax.mail.Multipart;
014: import javax.mail.internet.MimeMessage;
015: import javax.mail.internet.MimeBodyPart;
016: import javax.mail.internet.MimeMultipart;
017: import javax.activation.DataHandler;
018: import java.util.TimeZone;
019: import java.util.GregorianCalendar;
020: import java.io.FileInputStream;
021: import java.io.InputStream;
022: import java.security.cert.X509Certificate;
023: import java.security.KeyStore;
024:
025: /**
026: * EnvelopedSMIME class is used for creating and sending encrypted S/MIME
027: * message.<BR>
028: * <BR>
029: * Email message is in general composed of the content of the message and of one or
030: * more attachments. The content is visible part of the message, and attacments are
031: * mostly files or other binary data, which are not visible parts of message and
032: * which are used by email as a transport medium. In this implementation content
033: * can be represented in two different forms: <BR>
034: * <BR>
035: * <UL><LI>
036: * text/plain (only text withouth any formating) or
037: * </LI> <LI>
038: * text/html (html coded view of message)
039: * </LI></UL>
040: * Also, content can be absent, but than at least one attachment must be added.
041: * Content can be set on few manners. For text/plain type it can be done in time
042: * of construction with constructor designed special for creation of text/plain
043: * messages. Also, text content can be created by any of setContent() methods,
044: * if construction of object was done by other constructor which creates object
045: * with empty content. Construction with other constructor offers a few different
046: * posibilities for importing content data (File, InputStream, String) by using
047: * appropriate setContent() method. If method with four parameters is used, 3rd
048: * ant 4th parameters are not in use for text/plain message and could be set to
049: * null. For setting text/html content, construction of object should be done
050: * only by second mentioned constructor, which creates object with empty content.
051: * Content should be populated by html code with setContent() method. 3rd
052: * parameter is used for resolving relative addresses of resources in html
053: * code (images, movies...) and 4th parameter serves as data source for resources
054: * that are on special way addressed in html code. Also, there is a setContent()
055: * method which doesn't care about resources and which creates message content
056: * withouth them. For more information refer to setContent() methods.<BR>
057: * <BR>
058: * Message can contain any number of attachments. Also, message can
059: * be wihouth any attachment, but then content must be present. Every attachment
060: * should be added by performing single addAttachment() method. Attachments
061: * can be added from file or from InputStream. Mime-type which corresponds to
062: * particular attachment is obtained according to extension of file name
063: * (virtual or real file name) passed to addAttachment() method. File mime.types
064: * in META_INF directory contains list of mime-types and corresponding extensions
065: * which are used in determination of mime-type. File can be changed to satisfy
066: * secific requrements. For more information refer to addAttachmenttent()
067: * method.<BR>
068: * <BR>
069: * Encryption of message is performed by symmetric encryption with random
070: * generated symmetric key. This key is then encrypted by assymetric encryption
071: * with a recipient's public key, and sent together with encrypted message to
072: * recipient in CMS (Cryptographic Message Syntax) enveloped object. For all
073: * recipients of the message (if there is more than one) operation of encrypting
074: * symmetric key must be performed with his corresponding public key (from .cer
075: * file). Encryption can be performed by following algorithms and corresponding
076: * key sizes:<BR>
077: * <BR>
078: * RC2_CBC, 40 bits (default encryption)<BR>
079: * RC2_CBC, 64 bits<BR>
080: * RC2_CBC, 128 bits<BR>
081: * DES, 56 bits<BR>
082: * DES_EDE3_CBC, 128 bits<BR>
083: * DES_EDE3_CBC, 192 bits<BR>
084: * <BR>
085: * As a asymmetric algorithm, RSA algorithm is used.<BR>
086: * <BR>
087: */
088: public class EnvelopedSMIME extends BaseSMIMEObject implements
089: EnvelopedConstants {
090:
091: /**
092: * Simple constructor. Dynamically loads the BC and SUN provider necessary for
093: * cryptography processing. This constructor does not create MIME message
094: * object, so it is obligatory to invoke initMimeMessage() method after this
095: * constructor.
096: */
097: protected EnvelopedSMIME() {
098: super ();
099: }
100:
101: /**
102: * Initializes the JavaMail session for SMTP and the MimeMessage object for message
103: * which will be sent. Dynamically loads the BC and SUN provider necessary for
104: * cryptography processing. This constructor is used for creating message with
105: * text/plain content. For creating html formated content (text/html), other
106: * constructor should be used in combination with one of setContent methods.
107: * Note that after using this constructor setContent method can be used only
108: * if "content" argument of constructor was given as null, otherwise setContent
109: * method can't be used because content is already set as text/plain.
110: * @param smtpHost name of SMTP host used for sending email
111: * @param fromAddress email address of sender (FROM field in email header)
112: * @param subject subject of email (SUBJECT field in email header). This
113: * argument can be null, but email message will be sent withouth SUBJECT.
114: * @param content text/plain content of email message. This argument can be
115: * null, but later one of setContent() methods or one of addAttachment()
116: * methods should be called
117: * @param charset character set for passed subject and content. The given
118: * Unicode string will be charset-encoded using the specified charset. The
119: * charset is also used to set the "charset" parameter. For example German
120: * letters should be encoded by usage of 'ISO-8859-1' charset. If charset
121: * parameter is null and subject or content contains non US-ASCII characters,
122: * it will be encoded using the platform's default charset.
123: * @exception SMIMEException if smtpHost or fromAddress parameters are null.
124: * Also, it can be caused by non SMIMEException which is MessagingException.
125: */
126: public EnvelopedSMIME(String smtpHost, String fromAddress,
127: String subject, String content, String charset)
128: throws SMIMEException {
129: super (smtpHost, fromAddress, subject, content, charset);
130: }
131:
132: /**
133: * Initializes the JavaMail session for SMTP and the MimeMessage object for message
134: * which will be sent. Dynamically loads the BC and SUN provider necessary for
135: * cryptography processing. This constructor does not create content of message
136: * and it can be set later with one of setContent methods. Also, message can be
137: * left withouth content, but then at least one attachement must be added.
138: * @param smtpHost name of SMTP host used for sending email
139: * @param fromAddress email address of sender (FROM field in email header)
140: * @param subject subject of email (SUBJECT field in email header). This
141: * argument can be null, but email message will be sent withouth SUBJECT.
142: * @param charset character set for passed subject and content. The given
143: * Unicode string will be charset-encoded using the specified charset. The
144: * charset is also used to set the "charset" parameter. For example German
145: * letters should be encoded by usage of 'ISO-8859-1' charset. If charset
146: * parameter is null and subject or content contains non US-ASCII characters,
147: * it will be encoded using the platform's default charset.
148: * @exception SMIMEException if smtpHost or fromAddress parameters are null.
149: * Also, it can be caused by non SMIMEException which is MessagingException.
150: */
151: public EnvelopedSMIME(String smtpHost, String fromAddress,
152: String subject, String charset) throws SMIMEException {
153: super (smtpHost, fromAddress, subject, null, charset);
154: }
155:
156: /**
157: * Construction of message with external prepared MimeMessage object. Usage of
158: * this constructor disables usage of setContent() and addAttachment() methods.
159: * Also, all recipients (TO, CC or BCC type) must be declared again via
160: * setRecipient() method, even if they were previously set. Be very carefull
161: * with usage of this constructor because all MimeBodyPart objects and
162: * MimeMultipart objects used in construction of given MimeMessage object,
163: * must have correct defined Content header arguments, and contents. Contents
164: * must be formed in format which can be recognised and appropriate interpreted
165: * in the process of sending mail. If there is any special content object
166: * added to MimeBodyPart object or MimeMultipart object, the appropriate
167: * DataContent handler must be created for that object and set to corresponding
168: * BodyPart.
169: * @param mimeMessage external created MimeMessage object
170: * @exception SMIMEException if smtpHost or fromAddress parameter is null.
171: * Also, it can be caused by non SMIMEException which is MessagingException.
172: */
173: public EnvelopedSMIME(MimeMessage mimeMessage)
174: throws SMIMEException {
175: super (mimeMessage);
176: }
177:
178: /**
179: * Adds recipient address, type and .cer file of email recipient to encrypted
180: * message. At least one recipient must be declared as TO type.
181: * @param recipientAddress email address of recipent (fields TO or CC or BCC
182: * in email message header)
183: * @param type should be TO, CC or BCC
184: * @param cerFileName path and file name with certificate corresponding
185: * to recipient (file with .cer extension)
186: * @exception SMIMEException if type of addressing of the messages is not TO, CC,
187: * or BCC. Also it can be caused by non SMIMEException which is MessagingException.
188: */
189: public void addRecipient(String recipientAddress, String type,
190: String cerFileName) throws SMIMEException {
191: super .addRecipient(recipientAddress, type, cerFileName);
192: }
193:
194: /**
195: * Adds recipient address, type and recipient's certificate via KeyStore
196: * object and apropriate alias. Certificate information should be passed only
197: * in the case of Enveloped message or Signed and Enveloped message. Otherwise
198: * those arguments represent redundance values and they should be given as
199: * null.
200: * @param recipientAddress email address of recipent (fields TO or CC or BCC
201: * in email message header)
202: * @param type should be TO, CC or BCC
203: * @param kStore instance of KeyStore class which represents an in-memory
204: * collection of keys and certificates.
205: * @param alias alias name which corresponds to desired certificate. If alias
206: * is given as null, then reading results are unpredictable.
207: * @exception SMIMEException if type of addressing of messages is not TO, CC
208: * or BCC. Also, it can be caused by non SMIMEException which can be one of
209: * the following: IOException, MessagingException, FileNotFoundException,
210: * NoSuchProviderException, CertificateException.
211: */
212: public void addRecipient(String recipientAddress, String type,
213: KeyStore kStore, String alias) throws SMIMEException {
214: super .addRecipient(recipientAddress, type, kStore, alias);
215: }
216:
217: /**
218: * Adds recipient address, type and recipient's certificate via path to the
219: * KeyStore file, KeyStore type, password and apropriate alias. Certificate
220: * information should be passed only in the case of Enveloped message or
221: * Signed and Enveloped message. Otherwise those arguments represent
222: * redundance values and they should be given as null.
223: * @param recipientAddress email address of recipent (fields TO or CC or BCC
224: * in email message header)
225: * @param type should be TO, CC or BCC
226: * @param ksPath is path to the file representation of KeyStore which holds
227: * collection of keys and certificates. This file can be PKCS12 type (file
228: * with .p12 or .pfx extension) or can be key store of other types readable
229: * by 'BouncyCastle' or 'Sun' KeyStore implementation.
230: * @param ksType is type of KeyStore. It can be one of the following types:
231: * JKS for 'Sun' KeyStore, 'BKS', 'PKCS12' or 'UBER') for 'BouncyCastle'
232: * KeyStore. If ksType is given as null it will be assumed that .cer file is
233: * in use, and alias parameter will be ignored, so this method becomes
234: * equivalent to addRecipient() method which deal only with .cer files.
235: * @param password password used to access the corresponding private key,
236: * stored in given KeyStore file.
237: * @param alias alias name which corresponds to desired private key. If alias
238: * is given as null, then reading results are unpredictable.
239: * to recipient (file with .cer extension)
240: * @exception SMIMEException if type of addressing of messages is not TO, CC
241: * or BCC. Also, it can be caused by non SMIMEException which can be one of
242: * the following: IOException, MessagingException, FileNotFoundException,
243: * NoSuchProviderException, CertificateException.
244: */
245: public void addRecipient(String recipientAddress, String type,
246: String ksPath, String ksType, String password, String alias)
247: throws SMIMEException {
248: super .addRecipient(recipientAddress, type, ksPath, ksType,
249: password, alias);
250: }
251:
252: /**
253: * Envelopes message with default algorithm RC2_CBC, 40 bits.
254: * @exception SMIMEException if one of recipients is not declared as TO
255: * recipient, or if there is no message for enveloping. Also, it can be caused
256: * by non SMIMEException which can be one of the following: CertificateException,
257: * IOException, MessagingException or FileNotFoundException.
258: */
259: public void enveloping() throws SMIMEException {
260: this .enveloping(this .RC2_CBC, 40);
261: }
262:
263: /**
264: * Envelopes message with given algorithm name and key length.
265: * @param algorithmName name of chosen algorithm used for encryption
266: * @param keyLength key size in bits
267: * @exception SMIMEException if one of recipients is not declared as TO
268: * recipient, or if there is no message for enveloping. Also, it can be caused
269: * by non SMIMEException which can be one of the following: CertificateException,
270: * IOException, MessagingException or FileNotFoundException.
271: */
272: public void enveloping(String algorithmName, int keyLength)
273: throws SMIMEException {
274: try {
275: if (super .indicatorTo != true)
276: throw new SMIMEException(1043);
277:
278: if (!super .externalMessagePresence) { // external MimeMessage object presence cheking
279:
280: if (super .contentPresence
281: & super .bodyPartArray.size() == 1) { // message contains only content
282: if (super .bodyPartArray.elementAt(0) instanceof MimeBodyPart) { // text/plain message
283: MimeBodyPart contentBody = (MimeBodyPart) super .bodyPartArray
284: .elementAt(0);
285: super .message.setContent((String) contentBody
286: .getContent(), contentBody
287: .getContentType());
288: super .message
289: .setDisposition(super .message.INLINE);
290: } else
291: // text/html message
292: super .message
293: .setContent((MimeMultipart) super .bodyPartArray
294: .elementAt(0));
295: } else if (super .bodyPartArray.size() != 0) {
296: Multipart mp = new MimeMultipart();
297: for (int i = 0; i != super .bodyPartArray.size(); i++) {
298: if (super .bodyPartArray.elementAt(i) instanceof MimeMultipart) {
299: MimeBodyPart forMulti = new MimeBodyPart();
300: forMulti
301: .setContent((MimeMultipart) super .bodyPartArray
302: .elementAt(i));
303: mp.addBodyPart(forMulti);
304: } else
305: mp
306: .addBodyPart((MimeBodyPart) super .bodyPartArray
307: .elementAt(i));
308: }
309: super .message.setContent(mp);
310: } else
311: throw new SMIMEException(1044);
312:
313: }
314:
315: CMSEnvelopedDataSource es = new CMSEnvelopedDataSource(
316: super .message, algorithmName, keyLength);
317: for (int i = 0; i != super .certArray.size(); i++) {
318: X509Certificate cert = (X509Certificate) super .certArray
319: .elementAt(i);
320: es.addRecipient(cert);
321: }
322:
323: super .message.setDataHandler(new DataHandler(es));
324: super .message.saveChanges();
325:
326: super .message.setDescription("Enveloped SMIME message.");
327: super .message.setDisposition(super .message.ATTACHMENT);
328: TimeZone tz = TimeZone.getDefault(); // Sets date and time
329: GregorianCalendar cal = new GregorianCalendar(tz);
330: super .message.setSentDate(cal.getTime());
331:
332: clean();
333: } catch (Exception e) {
334: throw new SMIMEException(e);
335: }
336: }
337:
338: /**
339: * Returns SMIME Message. This method returns same object as getMimeMessage()
340: * method located in super class and will be removed in future release.
341: * @deprecated
342: * @return Enveloped S/MIME message
343: */
344: public MimeMessage getEnvelopedMessage() {
345: return super .message;
346: }
347:
348: /**
349: * Releases unnecessary memory.
350: */
351: private void clean() {
352: super .reset();
353: System.gc(); // Calling garbage collector
354: }
355:
356: }
|