001: /*
002: * Title: Oyster Project
003: * Description: S/MIME email sending capabilities
004: * @Author Vladimir Radisic
005: * @Version 2.1.6
006: */
007: package org.enhydra.oyster.smime;
008:
009: import org.enhydra.oyster.exception.SMIMEException;
010: import org.enhydra.oyster.cms.consts.CapabilitiesConstants;
011: import org.enhydra.oyster.util.PFXUtils;
012: import org.enhydra.oyster.crypto.consts.SignedConstants;
013: import java.util.Vector;
014: import java.io.FileInputStream;
015: import java.io.File;
016: import java.security.PrivateKey;
017: import java.security.KeyStore;
018: import java.security.cert.X509Certificate;
019: import java.security.cert.Certificate;
020: import javax.mail.internet.MimeMessage;
021:
022: /**
023: * This class is used as super class for SignedSMIME and SignedAndEnvelopedSMIME
024: * classes. It contains the common methods for this two classes.
025: */
026: public class BaseSignedSMIMEObject extends BaseSMIMEObject implements
027: SignedConstants {
028:
029: /**
030: * Simple constructor. Dynamically loads the BC and SUN provider necessary for
031: * cryptography processing. This constructor does not create MIME message
032: * object, so it is obligatory to invoke initMimeMessage() method after this
033: * constructor.
034: */
035: protected BaseSignedSMIMEObject() {
036: super ();
037: }
038:
039: /**
040: * Initializes the JavaMail session for SMTP and the MimeMessage object for message
041: * which will be sent. Dynamically loads the BC and SUN provider necessary for
042: * cryptography processing. This constructor is used for creating message with
043: * text/plain content. For creating html formated content (text/html), other
044: * constructor should be used in combination with one of setContent methods.
045: * Note that after using this constructor setContent method can be used only
046: * if "content" argument of constructor was given as null, otherwise setContent
047: * method can't be used because content is already set as text/plain.
048: * @param smtpHost name of SMTP host used for sending email
049: * @param fromAddress email address of sender (FROM field in email header)
050: * @param subject subject of email (SUBJECT field in email header). This
051: * argument can be null, but email message will be sent withouth SUBJECT.
052: * @param content text/plain content of email message. This argument can be
053: * null, but later one of setContent() methods or one of addAttachment()
054: * methods should be called
055: * @param charset character set for passed subject and content. The given
056: * Unicode string will be charset-encoded using the specified charset. The
057: * charset is also used to set the "charset" parameter. For example German
058: * letters should be encoded by usage of 'ISO-8859-1' charset. If charset
059: * parameter is null and subject or content contains non US-ASCII characters,
060: * it will be encoded using the platform's default charset.
061: * @exception SMIMEException if smtpHost or fromAddress parameters are null.
062: * Also, it can be caused by non SMIMEException which is MessagingException.
063: */
064: protected BaseSignedSMIMEObject(String smtpHost,
065: String fromAddress, String subject, String content,
066: String charset) throws SMIMEException {
067: super (smtpHost, fromAddress, subject, content, charset);
068: }
069:
070: /**
071: * Initializes the JavaMail session for SMTP and the MimeMessage object for message
072: * which will be sent. Dynamically loads the BC and SUN provider necessary for
073: * cryptography processing. This constructor does not create content of message
074: * and it can be set later with one of setContent methods. Also, message can be
075: * left withouth content, but then at least one attachement must be added.
076: * @param smtpHost name of SMTP host used for sending email
077: * @param fromAddress email address of sender (FROM field in email header)
078: * @param subject subject of email (SUBJECT field in email header). This
079: * argument can be null, but email message will be sent withouth SUBJECT.
080: * @param charset character set for passed subject and content. The given
081: * Unicode string will be charset-encoded using the specified charset. The
082: * charset is also used to set the "charset" parameter. For example German
083: * letters should be encoded by usage of 'ISO-8859-1' charset. If charset
084: * parameter is null and subject or content contains non US-ASCII characters,
085: * it will be encoded using the platform's default charset.
086: * @exception SMIMEException if smtpHost or fromAddress parameters are null.
087: * Also, it can be caused by non SMIMEException which is MessagingException.
088: */
089: protected BaseSignedSMIMEObject(String smtpHost,
090: String fromAddress, String subject, String charset)
091: throws SMIMEException {
092: super (smtpHost, fromAddress, subject, null, charset);
093: }
094:
095: /**
096: * Construction of message with external prepared MimeMessage object. Usage of
097: * this constructor disables usage of setContent() and addAttachment() methods.
098: * Also, all recipients (TO, CC or BCC type) must be declared again via
099: * setRecipient() method, even if they were previously set. Be very carefull
100: * with usage of this constructor because all MimeBodyPart objects and
101: * MimeMultipart objects used in construction of given MimeMessage object,
102: * must have correct defined Content header arguments, and contents. Contents
103: * must be formed in format which can be recognised and appropriate interpreted
104: * in the process of sending mail. If there is any special content object
105: * added to MimeBodyPart object or MimeMultipart object, the appropriate
106: * DataContent handler must be created for that object and set to corresponding
107: * BodyPart.
108: * @param mimeMessage external created MimeMessage object
109: * @exception SMIMEException if smtpHost or fromAddress parameter is null.
110: * Also, it can be caused by non SMIMEException which is MessagingException.
111: */
112: protected BaseSignedSMIMEObject(MimeMessage mimeMessage)
113: throws SMIMEException {
114: super (mimeMessage);
115: }
116:
117: /**
118: * Storage for .pfx files corresponding to appropriate signing session (used
119: * for first type of addSigner function).
120: */
121: protected Vector ksArray = new Vector(0, 1);
122:
123: /**
124: * Storage for digest algorithm corresponding to appropriate signing session
125: * (used for first type of addSigner function).
126: */
127: protected Vector digestArray = new Vector(0, 1);
128:
129: /**
130: * Storage for byte[2] grouped indicators (used for first type of addSigner
131: * function).
132: */
133: protected Vector including = new Vector(0, 1);
134:
135: /**
136: * Storage for certificate chain corresponding to appropriate signing session
137: * (used for second type of addSigner function)
138: */
139: protected Vector certChainArray = new Vector(0, 1);
140:
141: /**
142: * Storage for private key corresponding to appropriate signing session (used
143: * for second type of addSigner function)
144: */
145: protected Vector privKeyArray = new Vector(0, 1);
146:
147: /**
148: * Storage for digest algorithm corresponding to appropriate signing session
149: * (used for second type of addSigner function)
150: */
151: protected Vector digestArray2 = new Vector(0, 1);
152:
153: /**
154: * Storage for byte[2] grouped indicators (used for second type of addSigner
155: * function)
156: */
157: protected Vector including2 = new Vector(0, 1);
158:
159: /**
160: * Storage for additional certificates
161: */
162: protected Vector aditionalCerts = new Vector(0, 1);
163:
164: /**
165: * Temporary storage for capabilities (after method addSigner, this object is
166: * copied to capabilities or capabilities2).
167: */
168: protected Vector capabilitiesTemp = new Vector(6, 1);
169:
170: /**
171: * Storage for capabilities (used for first type of addSigner function)
172: */
173: protected Vector capabilities = new Vector(0, 1);
174:
175: /**
176: * Storage for capabilities (used for second type of addSigner function)
177: */
178: protected Vector capabilities2 = new Vector(0, 1);
179:
180: /**
181: * Sets Capabilities Attributes (method is optional, but if exists, must be
182: * performed before addSigner method). Depending on parameter type0 (algorithm
183: * group type), other parameter contains array of algoriithms from specific
184: * group of algorithms in client prefered usage order. Groups of algorithms
185: * are:<BR>
186: * SIGNATURE - MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA<BR>
187: * SYMMETRIC - RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3<BR>
188: * ENCIPHER - RSA<BR>
189: * DEFAULT - sets the default values for all three algorithm group types<BR>
190: * <BR>
191: * It is free to decide which algorithm will be included, or which group of algorithm
192: * will be included in Capabilities Attributes. If no groups are added, capabilities
193: * attributes won't be added to Signed Attributes. If two or more signers will
194: * sign the message, and their capabilities are different, this method should
195: * be performed before every signing if we wish to specify Capabilities
196: * Attributes for all particular signers. If type0 parameter is set as:<BR>
197: * <BR>
198: * setCapabilities (CapabilitiesConstants.DEFAULT, new String[0])<BR>
199: * <BR>
200: * it is equivalent to:<BR>
201: * <BR>
202: * setCapabilities(CapabilitiesConstants.SYMMETRIC, new String[] {CapabilitiesConstants.RC2_CBC_40});<BR>
203: * setCapabilities(CapabilitiesConstants.ENCIPHER, new String[] {CapabilitiesConstants.RSA });<BR>
204: * setCapabilities(CapabilitiesConstants.SIGNATURE, new String[] {CapabilitiesConstants.SHA1_WITH_RSA });<BR>
205: * <BR>
206: * @param type0 sets group of algorithms for capabilities attributes. It can be set
207: * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT.
208: * @param capability0 array of user prefered algorithms in user prrefered
209: * order for each capabilityes group.
210: * @exception SMIMEException if same group is added more than once, invalid
211: * group type is used, or group is added after DEFAULT option.
212: */
213: public void setCapabilities(String type0, String[] capability0)
214: throws SMIMEException {
215: capabilitiesTemp.setSize(6);
216:
217: String s = (String) capabilitiesTemp.get(0);
218: if (s != null && s.equals(CapabilitiesConstants.DEFAULT))
219: throw new SMIMEException(1047);
220:
221: if (capability0 == null)
222: capability0 = new String[0];
223:
224: if (type0.equals(CapabilitiesConstants.SYMMETRIC)) {
225: if (capabilitiesTemp.get(0) != null)
226: throw new SMIMEException(1045);
227: capabilitiesTemp.add(0, type0);
228: capabilitiesTemp.add(1, capability0);
229: } else if (type0.equals(CapabilitiesConstants.SIGNATURE)) {
230: if (capabilitiesTemp.get(2) != null)
231: throw new SMIMEException(1045);
232: capabilitiesTemp.add(2, type0);
233: capabilitiesTemp.add(3, capability0);
234: } else if (type0.equals(CapabilitiesConstants.ENCIPHER)) {
235: if (capabilitiesTemp.get(4) != null)
236: throw new SMIMEException(1045);
237: capabilitiesTemp.add(4, type0);
238: capabilitiesTemp.add(5, capability0);
239: } else if (type0.equals(CapabilitiesConstants.DEFAULT)) {
240: capabilitiesTemp.clear();
241: capabilitiesTemp.setSize(6);
242: capabilitiesTemp.add(0, type0);
243: capabilitiesTemp.add(1, capability0);
244: } else
245: throw new SMIMEException(1030);
246:
247: }
248:
249: /**
250: * Adds signer to signed and enveloped S/MIME message.
251: * @param pfxfileName path and file name with certificate and private key
252: * corresponding to the sender of the message (file with .p12 or .pfx extension)
253: * @param password used to access to .pfx or .p12 file
254: * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
255: * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
256: * @param includingCert including/not including certificates to signed
257: * message
258: * @param includingSignAttrib including/not including signed attributes
259: * to signed message. Must be set to true in case of implicit signing
260: * @exception SMIMEException caused by non SMIMEException which can be one of the
261: * following: FileNotFoundException, NoSuchProviderException, KeyStoreException
262: * CertificateException, NoSuchAlgorithmException or IOException.
263: */
264: public void addSigner(String pfxfileName, String password,
265: String signingAlg, boolean includingCert,
266: boolean includingSignAttrib) throws SMIMEException {
267: try {
268: char[] paswCh = password.toCharArray();
269: FileInputStream inPFX = new FileInputStream(pfxfileName);
270: KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
271: ks.load(inPFX, paswCh);
272: inPFX.close();
273: boolean[] incl = { includingCert, includingSignAttrib };
274: ksArray.addElement(ks);
275: digestArray.addElement(signingAlg);
276: including.addElement(incl);
277:
278: for (int i = 0; i != 6; i++) {
279: if (capabilitiesTemp.size() != 0) {
280: capabilities.addElement(capabilitiesTemp
281: .elementAt(i));
282: } else {
283: capabilities.addElement(null);
284: }
285: }
286: /* vr 16.03.1007.
287: if (capabilitiesTemp.size() != 0) {
288: for (int i = 0; i != capabilitiesTemp.size(); i++)
289: capabilities.addElement(capabilitiesTemp.elementAt(i));
290: }*/
291:
292: capabilitiesTemp.clear();
293: capabilitiesTemp.setSize(6);
294: } catch (Exception e) {
295: throw new SMIMEException(e);
296: }
297: }
298:
299: /**
300: * Adds signer to signed and enveloped S/MIME message.
301: * @param chain certificate chain. First certificate in array must be
302: * owner's certificate, and last certificate has to be root certificate
303: * @param privKey private key corresponding to owner's certificate (DSA
304: * or RSA depend on type of signing)
305: * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
306: * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
307: * @param includingCert including/not including certificates to signed
308: * message
309: * @param includingSignAttrib including/not including signed attributes
310: * to signed message. Must be set to true in case of implicit signing.
311: */
312: public void addSigner(X509Certificate[] chain, PrivateKey privKey,
313: String signingAlg, boolean includingCert,
314: boolean includingSignAttrib) {
315: boolean[] incl = { includingCert, includingSignAttrib };
316: certChainArray.addElement(chain);
317: privKeyArray.addElement(privKey);
318: digestArray2.addElement(signingAlg);
319: including2.addElement(incl);
320:
321: for (int i = 0; i != 6; i++) {
322: if (capabilitiesTemp.size() != 0) {
323: capabilities2.addElement(capabilitiesTemp.elementAt(i));
324: } else {
325: capabilities2.addElement(null);
326: }
327: }
328:
329: /* vr 16.03.1007.
330: if (capabilitiesTemp.size() != 0) {
331: for (int i = 0; i != capabilitiesTemp.size(); i++)
332: capabilities2.addElement(capabilitiesTemp.elementAt(i));
333: }*/
334:
335: capabilitiesTemp.clear();
336: capabilitiesTemp.setSize(6);
337: }
338:
339: /**
340: * Adds signer to signed and enveloped S/MIME message.
341: * @param kStore instance of KeyStore class which represents an in-memory
342: * collection of keys and certificates.
343: * @param password password used to access the corresponding private key,
344: * stored in given KeyStore object.
345: * @param alias alias name which corresponds to desired private key. If alias
346: * is given as null, then reading results are unpredictable.
347: * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
348: * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
349: * @param includingCert including/not including certificates to signed
350: * message
351: * @param includingSignAttrib including/not including signed attributes
352: * to signed message. Must be set to true in case of implicit signing.
353: * @exception SMIMEException caused by non SMIMEException which can be one
354: * of the following: KeyStoreException, UnrecoverableKeyException or
355: * NoSuchAlgorithmException.
356: */
357: public void addSigner(KeyStore kStore, String password,
358: String alias, String signingAlg, boolean includingCert,
359: boolean includingSignAttrib) throws SMIMEException {
360: try {
361: char[] paswCh = password.toCharArray();
362: X509Certificate[] chain = null;
363: PrivateKey privKey = null;
364:
365: if (alias != null) {
366: Certificate[] certs = kStore.getCertificateChain(alias);
367: if (certs != null && certs.length > 0) {
368: chain = new X509Certificate[certs.length];
369: for (int i = 0; i != certs.length; i++)
370: chain[i] = (X509Certificate) certs[i];
371: }
372: privKey = (PrivateKey) kStore.getKey(alias, paswCh);
373: } else {
374: chain = PFXUtils.getCertificateChain(kStore);
375: if (chain == null)
376: chain = PFXUtils.getAllX509Certificate(kStore);
377:
378: privKey = PFXUtils.getPrivateKey(kStore);
379: }
380:
381: this .addSigner(chain, privKey, signingAlg, includingCert,
382: includingSignAttrib);
383: } catch (Exception e) {
384: throw new SMIMEException(e);
385: }
386: }
387:
388: /**
389: * Adds signer to signed and enveloped S/MIME message.
390: * @param ksPath is path to the file representation of KeyStore which holds
391: * collection of keys and certificates. This file can be PKCS12 type (file
392: * with .p12 or .pfx extension) or can be key store of other types readable
393: * by 'BouncyCastle' or 'Sun' KeyStore implementation.
394: * @param ksType is type of KeyStore. It can be one of the following types:
395: * JKS for 'Sun' KeyStore, 'BKS', 'PKCS12' or 'UBER') for 'BouncyCastle'
396: * KeyStore. If ksType is given as null it will be assumed that PKCS12 type is
397: * in use, and alias parameter will be ignored, so this method becomes
398: * equivalent to addSigner() method which deal only with .pfx or .p12 files.
399: * @param password password used to access the corresponding private key,
400: * stored in given KeyStore file.
401: * @param alias alias name which corresponds to desired private key. If alias
402: * is given as null, then reading results are unpredictable.
403: * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
404: * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
405: * @param includingCert including/not including certificates to signed
406: * message
407: * @param includingSignAttrib including/not including signed attributes
408: * to signed message. Must be set to true in case of implicit signing.
409: * @exception SMIMEException if wrong path to KeyStore file ia given. Also,
410: * it can be caused by non SMIMEException which can be one of the following:
411: * FileNotFoundException, KeyStoreException, IOException, CertificateException
412: * or NoSuchAlgorithmException.
413: */
414: public void addSigner(String ksPath, String ksType,
415: String password, String alias, String signingAlg,
416: boolean includingCert, boolean includingSignAttrib)
417: throws SMIMEException {
418: char[] paswCh = password.toCharArray();
419: File fks = new File(ksPath);
420: if (!(fks.exists() && fks.isFile()))
421: throw new SMIMEException(1034);
422:
423: try {
424: if (ksType == null) // assumed PKCS12
425: this .addSigner(ksPath, password, signingAlg,
426: includingCert, includingSignAttrib);
427: else {
428: FileInputStream fis = new FileInputStream(fks);
429: KeyStore kStore = KeyStore.getInstance(ksType);
430: kStore.load(fis, paswCh);
431: fis.close();
432:
433: this .addSigner(kStore, password, alias, signingAlg,
434: includingCert, includingSignAttrib);
435: }
436: } catch (Exception e) {
437: throw new SMIMEException(e);
438: }
439: }
440:
441: /**
442: * Adds additional certificate to signed message.
443: * @param cert X509 certificate
444: */
445: public void addCertificate(X509Certificate cert) {
446: aditionalCerts.addElement(cert);
447: }
448:
449: /**
450: * Resets all attributes in BaseSignedSMIMEObject to their initial values. The
451: * attributes have the same values as when simple construcor is invoked. It
452: * means that after this method call, MIME message object is set to null, and
453: * it has to be rebuild again.
454: */
455: public void reset() {
456: super.reset();
457: this.ksArray.removeAllElements();
458: this.digestArray.removeAllElements();
459: this.including.removeAllElements();
460: this.certChainArray.removeAllElements();
461: this.privKeyArray.removeAllElements();
462: this.digestArray2.removeAllElements();
463: this.including2.removeAllElements();
464: this.aditionalCerts.removeAllElements();
465: this.capabilitiesTemp.removeAllElements();
466: this.capabilities.removeAllElements();
467: this.capabilities2.removeAllElements();
468: }
469:
470: }
|