KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > oyster > activation > CMSSignedDataSource


1 /*
2  * Title: Oyster Project
3  * Description: S/MIME email sending capabilities
4  * @Author Vladimir Radisic
5  * @Version 2.1.5
6  */

7
8
9 package org.enhydra.oyster.activation;
10
11 import org.enhydra.oyster.cms.*;
12 import org.enhydra.oyster.util.MimeAssist;
13 import org.enhydra.oyster.util.PFXUtils;
14 import org.enhydra.oyster.util.MimeAssist;
15 import org.enhydra.oyster.exception.SMIMEException;
16 import org.enhydra.oyster.exception.SMIMEIOException;
17 import javax.mail.internet.MimeMessage JavaDoc;
18 import javax.activation.DataSource JavaDoc;
19 import java.io.*;
20 import java.util.Vector JavaDoc;
21 import java.security.PrivateKey JavaDoc;
22 import java.security.KeyStore JavaDoc;
23 import java.security.cert.X509Certificate JavaDoc;
24
25 /**
26  * CMSSignedDataSource represents implementation of DataSource interfaces. It is
27  * used within MimeMessage as a source of data. Also, object of this class is
28  * used to create DER encoded Cryptographic Message Syntax (CMS) object
29  * represented in ASN.1 notation according to RFC2630. This object (CMS) is used
30  * as the source of data for MimeMessage in the process of sending signed message.
31  */

32 public class CMSSignedDataSource implements DataSource JavaDoc {
33
34 /**
35  * Container for messages.
36  */

37   private byte[] message;
38
39 /**
40  * Container for SignerInfos.
41  */

42   private SignerInfos sInfo;
43
44 /**
45  * Certificates container.
46  */

47   private Certificates certif;
48
49 /**
50  * Number of certificates stored in certificates container.
51  */

52   private int countCert = 0;
53
54 /**
55  * Capabilities attributes and their order
56  */

57   private String JavaDoc[] capabilities;
58
59 /**
60  * Decision: external (true) / internal (false) signing
61  */

62   private boolean externalSignature = true;
63
64 /**
65  * Used in setting digestAlgorithms field in Signed Data sequence:
66  * {SHA1,MD2,MD5}
67  */

68   private boolean[] typeOfDigest = { false, false, false };
69
70 /**
71  * Disables overlaping of capabilities for same group:
72  * (SYMMETRIC,SIGNATURE,ENCIPHER,DEFAULT)
73  */

74   private boolean[] enableOfCapabil = { false, false, false, false };
75
76 /**
77  * Boundary used in process of generation external signed message.
78  */

79   private String JavaDoc boundary = null;
80
81 /**
82  * Constructs CMS object for signing with Mime Message in form of
83  * byte array and with given value for type of CMSSignedDataSource (type of signing).
84  * Type can be external or internal signing.
85  * @param message0 message for encryption
86  * @param externalSignature0 true = external signing, false = internal
87  * signing
88  * @exception SMIMEException in case of failure in MimeMessageConvertor
89  * class which performes transformation from MimeMessage object to byte array.
90  * Also, it can be caused by problems in construction or work with some
91  * inner objects instantiated from classes that belong to
92  * org.enhydra.oyster.der or org.enhydra.oyster.cms packages used
93  * in other CMSEnvelopedObject constructor.
94  */

95   public CMSSignedDataSource (byte[] message0, boolean externalSignature0) throws SMIMEException
96   {
97     message = message0;
98 // org.enhydra.oyster.util.Display.printByteArrayAsHex(message0);
99
externalSignature = externalSignature0;
100     if(externalSignature)
101       boundary = "smime_bondary_" + Long.toString(System.currentTimeMillis());
102     sInfo = new SignerInfos();
103     certif = new Certificates();
104     capabilities = new String JavaDoc[18];
105   }
106
107
108 /**
109  * Constructs CMS object for signing with Mime Message in form of
110  * instance of MimeMessage class and with given value for type of CMSSignedDataSource
111  * (type of signing). Type can be external or internal signing.
112  * @param message0 message for encryption
113  * @param externalSignature0 true = external signing, false = internal
114  * signing
115  * @exception SMIMEException caused by problems in construction or work with
116  * some inner objects instantiated from classes that belong to
117  * org.enhydra.oyster.der or org.enhydra.oyster.cms packages used
118  * in other CMSEnvelopedObject constructor.
119  */

120   public CMSSignedDataSource (MimeMessage JavaDoc message0, boolean externalSignature0) throws SMIMEException
121   {
122     this( MimeAssist.messageConvertor(message0), externalSignature0 );
123   }
124
125 /**
126  * Sets Capabilities Attributes (method is optional, but if exists, must be
127  * performed before addSigner method). Depending on parameter type0, other five
128  * parameters make order in specific group of algorithms. Groups of algorithms
129  * with positions of specific algorithms are:<BR>
130  * (SIGNATURE, MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA, Unused field)<BR>
131  * (SYMMETRIC, RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3)<BR>
132  * (ENCIPHER, RSA, Unused field, Unused field, Unused field, Unused field)<BR>
133  * <BR>
134  * For example, if we wish to set Capabilities Attributes for symmetric algorithms
135  * in order: RC2 64 bits, RC2 40 bits and DES, encipher algorithm RSA (only possible
136  * in this version), and signature algorithms in order: SHA1 with RSA, MD5 with RSA
137  * and MD2 with RSA, we should make following lines of code<BR>
138  * <BR>
139  * setCapabilities ("SYMMETRIC", 2, 1, 0, 3, 0)<BR>
140  * setCapabilities ("ENCIPHER", 1, 0, 0, 0, 0)<BR>
141  * setCapabilities ("SIGNATURE", 3, 2, 1, 0, 0)<BR>
142  * <BR>
143  * 0 means exclusion of algorithm from the specified position in the method. It is
144  * free to decide which algorithm will be included, or which group of algorithm
145  * will be included in Capabilities Attributes. If no groups are added, capabilities
146  * attributes won't be added to Signed Attributes. If two or more signers will
147  * sign the message, and their capabilities are different, this method should
148  * be performed before every signing if we wish to specify Capabilities
149  * Attributes for all particular signers. If type0 parameter is set as:<BR>
150  * setCapabilities ("DEFAULT", 0, 0, 0, 0, 0)<BR>
151  * it is equivalent to:<BR>
152  * setCapabilities ("SYMMETRIC", 1, 0, 0, 0, 0)<BR>
153  * setCapabilities ("ENCIPHER", 0, 0, 1, 0, 0)<BR>
154  * setCapabilities ("SIGNATURE", 1, 0, 0, 0, 0)<BR>
155  * @param type0 sets group of algorithms for capabilities attributes. It can be set
156  * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT.
157  * @param par10 sets order in group of parameters, or exclude some algorithms
158  * from capabilities atributes. Can take values 1, 2, 3, 4 or 5 and 0 for
159  * exclusion of the particular algorithm.
160  * @param par20 same as for par10
161  * @param par30 same as for par10
162  * @param par40 same as for par10
163  * @param par50 same as for par10
164  * @exception SMIMEException if method is performed more than three times for one signer,
165  * or in case of wrong values of parameters.
166  */

167   public void setCapabilities (String JavaDoc type0, int par10, int par20, int par30, int par40, int par50) throws SMIMEException {
168     if (par10 > 5 | par10 < 0 | par20 > 5 | par20 < 0 | par30 > 5 | par30 < 0 | par40 > 5 | par40 < 0 | par50 > 5 | par50 < 0)
169       throw new SMIMEException(this, 1028);
170     if (type0.equalsIgnoreCase("SYMMETRIC")) {
171       if (enableOfCapabil[0] == true | enableOfCapabil[3] == true)
172         throw new SMIMEException(this, 1029);
173       capabilities[par10] = "RC2_CBC_40"; // RC2 40 bits algorithm
174
capabilities[par20] = "RC2_CBC_64"; // RC2 64 bits algorithm
175
capabilities[par30] = "RC2_CBC_128"; // RC2 128 bits algorithm
176
capabilities[par40] = "DES"; // DES algorithm
177
capabilities[par50] = "DES_EDE3_CBC"; // DES_EDE3 algorithm
178
capabilities[0] = null; // Cleaning first element (this element is not important)
179
enableOfCapabil[0] = true;
180     }
181     else if (type0.equalsIgnoreCase("SIGNATURE")) {
182       if (enableOfCapabil[1] == true | enableOfCapabil[3] == true)
183         throw new SMIMEException(this, 1029);
184       capabilities[par10 + 6] = "MD2_WITH_RSA"; // MD2 with RSA algorithm
185
capabilities[par20 + 6] = "MD5_WITH_RSA"; // MD5 with RSA algorithm
186
capabilities[par30 + 6] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm
187
capabilities[par40 + 6] = "SHA1_WITH_DSA"; // SHA1 with DSA algorithm
188
capabilities[par50 + 6] = null; // For future use
189
capabilities[6] = null; // Cleaning first element (this element is not important)
190
enableOfCapabil[1] = true;
191     }
192     else if (type0.equalsIgnoreCase("ENCIPHER")) {
193       if (enableOfCapabil[2] == true | enableOfCapabil[3] == true)
194         throw new SMIMEException(this, 1029);
195       capabilities[par10 + 12] = "RSA"; // RSA Encription
196
capabilities[par20 + 12] = null; // For future use
197
capabilities[par30 + 12] = null; // For future use
198
capabilities[par40 + 12] = null; // For future use
199
capabilities[par50 + 12] = null; // For future use
200
capabilities[12] = null; // Cleaning first element (this element is not important)
201
enableOfCapabil[2] = true;
202     }
203     else if (type0.equals("DEFAULT")) {
204       for (int i = 0; i != capabilities.length; i++)
205         capabilities[i] = null;
206       capabilities[13] = "RSA"; // RSA Encription
207
capabilities[9] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm
208
capabilities[1] = "RC2_CBC_40"; // RC2 40 bits algorithm
209
enableOfCapabil[3] = true;
210     }
211     else
212       throw new SMIMEException(this, 1030);
213   }
214
215 /**
216  * Adds Signer. This method must be performed at least once.
217  * @param pfx0 contains information from signer's .pfx or .p12 file
218  * @param includingCert0 true = automatically including all certificates from pfx0
219  * false = no certificate will be added
220  * @param includingSignAttrib0 true = signed attributes will be included, false
221  * = signed attributes will not be included
222  * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA,
223  * MD5_WITH_RSA or SHA1_WITH_DSA)
224  * @exception SMIMEException in case of wrong type of digest algorithm, or in
225  * case of problems with manipulation with .pfx or .p12 file in PFXUtils class.
226  * Also, it can be caused by problems in construction or work with some inner
227  * objects from org.enhydra.oyster.der or org.enhydra.oyster.cms package.
228  */

229   public void addSigner (KeyStore JavaDoc pfx0, boolean includingCert0, boolean includingSignAttrib0, String JavaDoc signingAlg0) throws SMIMEException {
230
231     X509Certificate JavaDoc[] chain = PFXUtils.getCertificateChain(pfx0);
232     if(chain == null)
233       chain = PFXUtils.getAllX509Certificate(pfx0);
234
235     PrivateKey JavaDoc pKey = PFXUtils.getPrivateKey(pfx0);
236
237     this.addSigner(chain, pKey, includingCert0, includingSignAttrib0, signingAlg0);
238   }
239
240 /**
241  * Adds Signer. This method must be performed at least once.
242  * @param chain0 signer's certificates chain. First certificate in chain
243  * must be owner's.
244  * @param privKey0 signer's private key (DSA or RSA depend on type of signing)
245  * @param includingCert0 true = automatically including all certificates from pfx0
246  * false = no certificate will be added
247  * @param includingSignAttrib0 true = signed attributes will be included, false
248  * = signed attributes will not be included
249  * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA,
250  * MD5_WITH_RSA or SHA1_WITH_DSA)
251  * @exception SMIMEException in case of wrong type of digest algorithm. Also,
252  * it can be caused by problems in construction or work with some inner
253  * objects from org.enhydra.oyster.der or org.enhydra.oyster.cms package.
254  */

255   public void addSigner (X509Certificate JavaDoc[] chain0, PrivateKey JavaDoc privKey0, boolean includingCert0, boolean includingSignAttrib0, String JavaDoc signingAlg0) throws SMIMEException {
256     String JavaDoc digestAlg = null;
257     if (signingAlg0.equalsIgnoreCase("SHA1_WITH_RSA")) {
258       typeOfDigest[0] = true;
259       digestAlg = "SHA1";
260     }
261     else if (signingAlg0.equalsIgnoreCase("SHA1_WITH_DSA")) {
262       typeOfDigest[0] = true;
263       digestAlg = "SHA1";
264     }
265     else if (signingAlg0.equalsIgnoreCase("MD2_WITH_RSA")) {
266       typeOfDigest[1] = true;
267       digestAlg = "MD2";
268     }
269     else if (signingAlg0.equalsIgnoreCase("MD5_WITH_RSA")) {
270       typeOfDigest[2] = true;
271       digestAlg = "MD5";
272     }
273     else
274       throw new SMIMEException(this, 1031);
275     SignedAttributes attrib = null; // Signed attributes remain null if includingSignAttrib0==false
276
if (includingSignAttrib0 == true) // Creating signers info with signed attributes
277
{
278       attrib = new SignedAttributes(); // Creating container of signed attrinutes
279
ContentTypeAttribute cont = new ContentTypeAttribute("ID_DATA", "NAME_STRING");
280       attrib.addSignedAttribute(cont.getDEREncoded());
281       SigningTimeAttribute time = new SigningTimeAttribute();
282       attrib.addSignedAttribute(time.getDEREncoded());
283       boolean include = false; // Check for capabilities attributes existing
284
for (int i = capabilities.length; i != 0; i--) {
285         if (capabilities[i - 1] != null) {
286           include = true;
287           break;
288         }
289       }
290       if (include == true) {
291         CapabilitiesAttribute cap = new CapabilitiesAttribute(capabilities);
292         attrib.addSignedAttribute(cap.getDEREncoded());
293         for (int i = capabilities.length; i != 0; i--) // resets capabilities
294
capabilities[i - 1] = null;
295       }
296       MessageDigestAttribute dig = new MessageDigestAttribute(message, digestAlg);
297       attrib.addSignedAttribute(dig.getDEREncoded());
298     }
299     sInfo.addSigner(message, chain0[0], privKey0, attrib, signingAlg0); // First certificate in chain must be certificate asociated with owners of private key
300
if (includingCert0 == true) {
301       for (int i = 0; i != chain0.length; i++) // Adding certificates from certificate chain
302
{
303         certif.addCertificate(chain0[i]);
304         countCert++;
305       }
306     }
307     for (int i = 0; i != enableOfCapabil.length; i++) // reset to enable other combination of capabilities for other signers
308
enableOfCapabil[i] = false;
309   }
310
311 /**
312  * Adds the Certificate
313  * @param cert0 X509 certificate
314  * @exception SMIMEException thrown in inner object which is instance of the class
315  * org.enhydra.oyster.cms.Certificates.
316  */

317   public void addCertificate (X509Certificate JavaDoc cert0) throws SMIMEException {
318     certif.addCertificate(cert0);
319     countCert++;
320   }
321
322 /**
323  * Returns complete DER encoded CMS Signed Object
324  * @return DER encoded CMS Signed Object represented as byte array
325  * @exception SMIMEException caused by problems in construction or dealing
326  * with some inner objects instantiated from classes that belong to
327  * org.enhydra.oyster.der or org.enhydra.oyster.cms packages.
328  */

329   public byte[] getCMSSignedObject() throws SMIMEException {
330     SignedData signData = new SignedData(); // Container for signed data sub object
331
signData.addCMSVersion(new CMSVersion(1).getDEREncoded());
332     DigestAlgorithmIdentifiers digAlg = new DigestAlgorithmIdentifiers();
333     if (typeOfDigest[0] == true)
334       digAlg.addDigestAlgIdNullPar("SHA1", "NAME_STRING");
335     if (typeOfDigest[1] == true)
336       digAlg.addDigestAlgIdNullPar("MD2", "NAME_STRING");
337     if (typeOfDigest[2] == true)
338       digAlg.addDigestAlgIdNullPar("MD5", "NAME_STRING");
339     signData.addDigestAlgorithm(digAlg.getDEREncoded());
340     EncapsulatedContentInfo enc = new EncapsulatedContentInfo();
341     ContentTypeIdentifier cont = new ContentTypeIdentifier("ID_DATA", "NAME_STRING");
342     enc.addContentType(cont.getDEREncoded());
343     if (externalSignature == false) {
344       enc.addEncapsulatedContent(message);
345     }
346     signData.addEncapsulatedContentInfo(enc.getDEREncoded());
347     if (countCert != 0)
348       signData.addCertificate(certif.getDEREncoded());
349     signData.addSignerInfos(sInfo.getDEREncoded());
350     Content signedContent = new Content(signData.getDEREncoded(), true); // Filling signed data content in context specific DER object
351
ContentInfo cmsObjectSignedData = new ContentInfo();
352     ContentTypeIdentifier contentTypeSignDataId = new ContentTypeIdentifier("ID_SIGNEDDATA", "NAME_STRING"); // Creating the Content Type
353
cmsObjectSignedData.addContentType(contentTypeSignDataId.getDEREncoded());
354     cmsObjectSignedData.addContent(signedContent.getDEREncoded());
355     return cmsObjectSignedData.getDEREncoded();
356   }
357
358 /**
359  * Returns complete DER encoded CMS Signed Object with BASE64 encoding
360  * @return DER encoded CMS Signed Object represented as byte array with
361  * performed BASE64 encoding.
362  * @exception SMIMEException in case of failure in Base64 encoding performed
363  * on the generated SMIME message byte array by method ofMimeAssist class. Also,
364  * it can be caused by problems in construction or work with some inner objects
365  * instantiated from classes that belong to org.enhydra.oyster.der or
366  * org.enhydra.oyster.cms packages used in getCMSSignedDataSource() method.
367  */

368   public byte[] getBASE64CMSSignedObject() throws SMIMEException {
369     return MimeAssist.getBASE64WithBreakOn76(this.getCMSSignedObject());
370   }
371
372
373 /**
374  * Returns "micalg" parameter used in Content-Type of external signing
375  * @return String representation of micalg parameter.
376  */

377   private String JavaDoc getMicalg() {
378     int sum = 0;
379     if(typeOfDigest[0])
380       sum = sum +1;
381     if(typeOfDigest[1])
382       sum = sum +10;
383     if(typeOfDigest[2])
384       sum = sum +100;
385
386      switch (sum) {
387       case 0:
388         return "\"unknown\"";
389       case 1:
390         return "\"sha1\"";
391       case 10:
392         return "\"md2\"";
393       case 100:
394         return "\"md5\"";
395       case 11:
396         return "\"sha1,md2\"";
397       case 101:
398         return "\"sha1,md5\"";
399       case 110:
400         return "\"md2,md5\"";
401       case 111:
402         return "\"sha1,md2,md5\"";
403       }
404     return "\"unknown\"";
405   }
406
407
408 /**
409  * Returns composed message for external signing.
410  * @return External signed message represented as byte array.
411  * @exception SMIMEException in case of failure in Base64 encoding performed
412  * on the generated SMIME message byte array by method ofMimeAssist class. Also,
413  * it can be caused by problems in construction or work with some inner objects
414  * instantiated from classes that belong to org.enhydra.oyster.der or
415  * org.enhydra.oyster.cms packages used in getCMSSignedDataSource() method.
416  */

417   private byte[] getExternalSignedMessage() throws SMIMEException {
418
419   try {
420     String JavaDoc extMessage =
421         "--" + boundary + "\r\n" +
422         new String JavaDoc(this.message,"ISO-8859-1") + "\r\n" +
423         "--" + boundary + "\r\n" +
424         "Content-Type: application/x-pkcs7-signature; name=smime.p7s" + "\r\n" +
425         "Content-Transfer-Encoding: base64" + "\r\n" +
426         "Content-Disposition: attachment; filename=smime.p7s" + "\r\n" + "\r\n" +
427         new String JavaDoc(this.getBASE64CMSSignedObject(),"ISO-8859-1") + "\r\n" +
428         "--" + boundary + "--";
429
430     return extMessage.getBytes("ISO-8859-1");
431     }
432     catch(Exception JavaDoc e) {
433       throw SMIMEException.getInstance(this, e, "getExternalSignedMessage");
434     }
435   }
436
437
438 /**
439  * Implements getContentType method from DataSource interface
440  * @return Content-Type for MIME message header field
441  */

442   public String JavaDoc getContentType() {
443     if (!externalSignature)
444       // For new version of mail clients: "application/pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\""
445
return "application/x-pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\"";
446     else
447       // For new version of mail clients protocol: "application/pkcs7-signature; name=\"smime.p7s\""
448
return "multipart/signed;" + "\r\n" +
449                   " protocol=\"application/x-pkcs7-signature\";" + "\r\n" +
450                   " micalg=" + this.getMicalg() + "; boundary=" + boundary;
451
452   }
453
454 /**
455  * Implements getInputStream method from DataSource interface
456  * @return CMS signed object
457  * @exception SMIMEIOException thrown as result of SMIMEException
458  */

459   public InputStream getInputStream() throws SMIMEIOException {
460     try {
461     if (!externalSignature)
462       return new ByteArrayInputStream(getCMSSignedObject());
463     else
464       return new ByteArrayInputStream(getExternalSignedMessage());
465     } catch (SMIMEException e) {
466         throw new SMIMEIOException(e);
467     }
468   }
469
470 /**
471  * Implements getName method from DataSource interface
472  * @return Name: SignedDataContentInfo
473  */

474   public String JavaDoc getName () {
475     return "SignedDataContentInfo";
476   }
477
478 /**
479  * Implements getOutputStream method from DataSource interface. This
480  * method is not in use.
481  * @return nothing
482  * @exception IOException is always thrown when this method is used.
483  */

484   public OutputStream getOutputStream () throws IOException {
485     throw new IOException("SignedDataContentInfo does not support getOutputStream()");
486   }
487 }
488
489
490
491
Popular Tags