1 /*
2 * Title: S/MIME Project
3 * Description: S/MIME email sending capabilities
4 * @Author Vladimir Radisic
5 * @Version 2.0.1
6 */
7
8
9 package org.webdocwf.util.smime.activation;
10
11
12 import org.webdocwf.util.smime.cms.*;
13 import org.webdocwf.util.smime.util.MimeAssist;
14 import org.webdocwf.util.smime.util.PFXUtils;
15 import org.webdocwf.util.smime.util.MimeAssist;
16 import org.webdocwf.util.smime.exception.SMIMEException;
17 import org.webdocwf.util.smime.exception.SMIMEIOException;
18 import javax.mail.internet.MimeMessage;
19 import javax.activation.DataSource;
20 import java.io.*;
21 import java.util.Vector;
22 import java.security.PrivateKey;
23 import java.security.KeyStore;
24 import java.security.cert.X509Certificate;
25
26
27 /***
28 * CMSSignedDataSource represents implementation of DataSource interfaces. It is
29 * used within MimeMessage as a source of data. Also, object of this class is
30 * used to create DER encoded Cryptographic Message Syntax (CMS) object
31 * represented in ASN.1 notation according to RFC2630. This object (CMS) is used
32 * as the source of data for MimeMessage in the process of sending signed message.
33 */
34 public class CMSSignedDataSource implements DataSource {
35
36 /***
37 * Container for messages.
38 */
39 private byte[] message;
40
41 /***
42 * Container for SignerInfos.
43 */
44 private SignerInfos sInfo;
45
46 /***
47 * Certificates container.
48 */
49 private Certificates certif;
50
51 /***
52 * Number of certificates stored in certificates container.
53 */
54 private int countCert = 0;
55
56 /***
57 * Capabilities attributes and their order
58 */
59 private String[] capabilities;
60
61 /***
62 * Decision: external (true) / internal (false) signing
63 */
64 private boolean externalSignature = true;
65
66 /***
67 * Used in setting digestAlgorithms field in Signed Data sequence:
68 * {SHA1,MD2,MD5}
69 */
70 private boolean[] typeOfDigest = { false, false, false };
71
72 /***
73 * Disables overlaping of capabilities for same group:
74 * (SYMMETRIC,SIGNATURE,ENCIPHER,DEFAULT)
75 */
76 private boolean[] enableOfCapabil = { false, false, false, false };
77
78 /***
79 * Boundary used in process of generation external signed message.
80 */
81 private String boundary = null;
82
83 /***
84 * Constructs CMS object for signing with Mime Message in form of
85 * byte array and with given value for type of CMSSignedDataSource (type of signing).
86 * Type can be external or internal signing.
87 * @param message0 message for encryption
88 * @param externalSignature0 true = external signing, false = internal
89 * signing
90 * @exception SMIMEException in case of failure in MimeMessageConvertor
91 * class which performes transformation from MimeMessage object to byte array.
92 * Also, it can be caused by problems in construction or work with some
93 * inner objects instantiated from classes that belong to
94 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages used
95 * in other CMSEnvelopedObject constructor.
96 */
97 public CMSSignedDataSource(byte[] message0, boolean externalSignature0) throws SMIMEException {
98 message = 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[18];
105 }
106
107 /***
108 * Constructs CMS object for signing with Mime Message in form of
109 * instance of MimeMessage class and with given value for type of CMSSignedDataSource
110 * (type of signing). Type can be external or internal signing.
111 * @param message0 message for encryption
112 * @param externalSignature0 true = external signing, false = internal
113 * signing
114 * @exception SMIMEException caused by problems in construction or work with
115 * some inner objects instantiated from classes that belong to
116 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages used
117 * in other CMSEnvelopedObject constructor.
118 */
119 public CMSSignedDataSource(MimeMessage message0, boolean externalSignature0) throws SMIMEException {
120 this(MimeAssist.messageConvertor(message0), externalSignature0);
121 }
122
123 /***
124 * Sets Capabilities Attributes (method is optional, but if exists, must be
125 * performed before addSigner method). Depending on parameter type0, other five
126 * parameters make order in specific group of algorithms. Groups of algorithms
127 * with positions of specific algorithms are:<BR>
128 * (SIGNATURE, MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA, Unused field)<BR>
129 * (SYMMETRIC, RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3)<BR>
130 * (ENCIPHER, RSA, Unused field, Unused field, Unused field, Unused field)<BR>
131 * <BR>
132 * For example, if we wish to set Capabilities Attributes for symmetric algorithms
133 * in order: RC2 64 bits, RC2 40 bits and DES, encipher algorithm RSA (only possible
134 * in this version), and signature algorithms in order: SHA1 with RSA, MD5 with RSA
135 * and MD2 with RSA, we should make following lines of code<BR>
136 * <BR>
137 * setCapabilities ("SYMMETRIC", 2, 1, 0, 3, 0)<BR>
138 * setCapabilities ("ENCIPHER", 1, 0, 0, 0, 0)<BR>
139 * setCapabilities ("SIGNATURE", 3, 2, 1, 0, 0)<BR>
140 * <BR>
141 * 0 means exclusion of algorithm from the specified position in the method. It is
142 * free to decide which algorithm will be included, or which group of algorithm
143 * will be included in Capabilities Attributes. If no groups are added, capabilities
144 * attributes won't be added to Signed Attributes. If two or more signers will
145 * sign the message, and their capabilities are different, this method should
146 * be performed before every signing if we wish to specify Capabilities
147 * Attributes for all particular signers. If type0 parameter is set as:<BR>
148 * setCapabilities ("DEFAULT", 0, 0, 0, 0, 0)<BR>
149 * it is equivalent to:<BR>
150 * setCapabilities ("SYMMETRIC", 1, 0, 0, 0, 0)<BR>
151 * setCapabilities ("ENCIPHER", 0, 0, 1, 0, 0)<BR>
152 * setCapabilities ("SIGNATURE", 1, 0, 0, 0, 0)<BR>
153 * @param type0 sets group of algorithms for capabilities attributes. It can be set
154 * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT.
155 * @param par10 sets order in group of parameters, or exclude some algorithms
156 * from capabilities atributes. Can take values 1, 2, 3, 4 or 5 and 0 for
157 * exclusion of the particular algorithm.
158 * @param par20 same as for par10
159 * @param par30 same as for par10
160 * @param par40 same as for par10
161 * @param par50 same as for par10
162 * @exception SMIMEException if method is performed more than three times for one signer,
163 * or in case of wrong values of parameters.
164 */
165 public void setCapabilities(String type0, int par10, int par20, int par30, int par40, int par50) throws SMIMEException {
166 if (par10 > 5 | par10 < 0 | par20 > 5 | par20 < 0 | par30 > 5 | par30 < 0 | par40 > 5 | par40 < 0 | par50 > 5 | par50 < 0)
167 throw new SMIMEException(this, 1028);
168 if (type0.equalsIgnoreCase("SYMMETRIC")) {
169 if (enableOfCapabil[0] == true | enableOfCapabil[3] == true)
170 throw new SMIMEException(this, 1029);
171 capabilities[par10] = "RC2_CBC_40"; // RC2 40 bits algorithm
172 capabilities[par20] = "RC2_CBC_64"; // RC2 64 bits algorithm
173 capabilities[par30] = "RC2_CBC_128"; // RC2 128 bits algorithm
174 capabilities[par40] = "DES"; // DES algorithm
175 capabilities[par50] = "DES_EDE3_CBC"; // DES_EDE3 algorithm
176 capabilities[0] = null; // Cleaning first element (this element is not important)
177 enableOfCapabil[0] = true;
178 } else if (type0.equalsIgnoreCase("SIGNATURE")) {
179 if (enableOfCapabil[1] == true | enableOfCapabil[3] == true)
180 throw new SMIMEException(this, 1029);
181 capabilities[par10 + 6] = "MD2_WITH_RSA"; // MD2 with RSA algorithm
182 capabilities[par20 + 6] = "MD5_WITH_RSA"; // MD5 with RSA algorithm
183 capabilities[par30 + 6] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm
184 capabilities[par40 + 6] = "SHA1_WITH_DSA"; // SHA1 with DSA algorithm
185 capabilities[par50 + 6] = null; // For future use
186 capabilities[6] = null; // Cleaning first element (this element is not important)
187 enableOfCapabil[1] = true;
188 } else if (type0.equalsIgnoreCase("ENCIPHER")) {
189 if (enableOfCapabil[2] == true | enableOfCapabil[3] == true)
190 throw new SMIMEException(this, 1029);
191 capabilities[par10 + 12] = "RSA"; // RSA Encription
192 capabilities[par20 + 12] = null; // For future use
193 capabilities[par30 + 12] = null; // For future use
194 capabilities[par40 + 12] = null; // For future use
195 capabilities[par50 + 12] = null; // For future use
196 capabilities[12] = null; // Cleaning first element (this element is not important)
197 enableOfCapabil[2] = true;
198 } else if (type0.equals("DEFAULT")) {
199 for (int i = 0; i != capabilities.length; i++)
200 capabilities[i] = null;
201 capabilities[13] = "RSA"; // RSA Encription
202 capabilities[9] = "SHA1_WITH_RSA"; // SHA1 with RSA algorithm
203 capabilities[1] = "RC2_CBC_40"; // RC2 40 bits algorithm
204 enableOfCapabil[3] = true;
205 } else
206 throw new SMIMEException(this, 1030);
207 }
208
209 /***
210 * Adds Signer. This method must be performed at least once.
211 * @param pfx0 contains information from signer's .pfx or .p12 file
212 * @param includingCert0 true = automatically including all certificates from pfx0
213 * false = no certificate will be added
214 * @param includingSignAttrib0 true = signed attributes will be included, false
215 * = signed attributes will not be included
216 * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA,
217 * MD5_WITH_RSA or SHA1_WITH_DSA)
218 * @exception SMIMEException in case of wrong type of digest algorithm, or in
219 * case of problems with manipulation with .pfx or .p12 file in PFXUtils class.
220 * Also, it can be caused by problems in construction or work with some inner
221 * objects from org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms package.
222 */
223 public void addSigner(KeyStore pfx0, boolean includingCert0, boolean includingSignAttrib0, String signingAlg0) throws SMIMEException {
224
225 X509Certificate[] chain = PFXUtils.getCertificateChain(pfx0);
226
227 if (chain == null)
228 chain = PFXUtils.getAllX509Certificate(pfx0);
229
230 PrivateKey pKey = PFXUtils.getPrivateKey(pfx0);
231
232 this.addSigner(chain, pKey, includingCert0, includingSignAttrib0, signingAlg0);
233 }
234
235 /***
236 * Adds Signer. This method must be performed at least once.
237 * @param chain0 signer's certificates chain. First certificate in chain
238 * must be owner's.
239 * @param privKey0 signer's private key (DSA or RSA depend on type of signing)
240 * @param includingCert0 true = automatically including all certificates from pfx0
241 * false = no certificate will be added
242 * @param includingSignAttrib0 true = signed attributes will be included, false
243 * = signed attributes will not be included
244 * @param signingAlg0 used for signing (can be SHA1_WITH_RSA, MD2_WITH_RSA,
245 * MD5_WITH_RSA or SHA1_WITH_DSA)
246 * @exception SMIMEException in case of wrong type of digest algorithm. Also,
247 * it can be caused by problems in construction or work with some inner
248 * objects from org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms package.
249 */
250 public void addSigner(X509Certificate[] chain0, PrivateKey privKey0, boolean includingCert0, boolean includingSignAttrib0, String signingAlg0) throws SMIMEException {
251 String digestAlg = null;
252
253 if (signingAlg0.equalsIgnoreCase("SHA1_WITH_RSA")) {
254 typeOfDigest[0] = true;
255 digestAlg = "SHA1";
256 } else if (signingAlg0.equalsIgnoreCase("SHA1_WITH_DSA")) {
257 typeOfDigest[0] = true;
258 digestAlg = "SHA1";
259 } else if (signingAlg0.equalsIgnoreCase("MD2_WITH_RSA")) {
260 typeOfDigest[1] = true;
261 digestAlg = "MD2";
262 } else if (signingAlg0.equalsIgnoreCase("MD5_WITH_RSA")) {
263 typeOfDigest[2] = true;
264 digestAlg = "MD5";
265 } else
266 throw new SMIMEException(this, 1031);
267 SignedAttributes attrib = null; // Signed attributes remain null if includingSignAttrib0==false
268
269 if (includingSignAttrib0 == true) // Creating signers info with signed attributes
270 {
271 attrib = new SignedAttributes(); // Creating container of signed attrinutes
272 ContentTypeAttribute cont = new ContentTypeAttribute("ID_DATA", "NAME_STRING");
273
274 attrib.addSignedAttribute(cont.getDEREncoded());
275 SigningTimeAttribute time = new SigningTimeAttribute();
276
277 attrib.addSignedAttribute(time.getDEREncoded());
278 boolean include = false; // Check for capabilities attributes existing
279
280 for (int i = capabilities.length; i != 0; i--) {
281 if (capabilities[i - 1] != null) {
282 include = true;
283 break;
284 }
285 }
286 if (include == true) {
287 CapabilitiesAttribute cap = new CapabilitiesAttribute(capabilities);
288
289 attrib.addSignedAttribute(cap.getDEREncoded());
290 for (int i = capabilities.length; i != 0; i--) // resets capabilities
291 capabilities[i - 1] = null;
292 }
293 MessageDigestAttribute dig = new MessageDigestAttribute(message, digestAlg);
294
295 attrib.addSignedAttribute(dig.getDEREncoded());
296 }
297 sInfo.addSigner(message, chain0[0], privKey0, attrib, signingAlg0); // First certificate in chain must be certificate asociated with owners of private key
298 if (includingCert0 == true) {
299 for (int i = 0; i != chain0.length; i++) // Adding certificates from certificate chain
300 {
301 certif.addCertificate(chain0[i]);
302 countCert++;
303 }
304 }
305 for (int i = 0; i != enableOfCapabil.length; i++) // reset to enable other combination of capabilities for other signers
306 enableOfCapabil[i] = false;
307 }
308
309 /***
310 * Adds the Certificate
311 * @param cert0 X509 certificate
312 * @exception SMIMEException thrown in inner object which is instance of the class
313 * org.webdocwf.util.smime.cms.Certificates.
314 */
315 public void addCertificate(X509Certificate cert0) throws SMIMEException {
316 certif.addCertificate(cert0);
317 countCert++;
318 }
319
320 /***
321 * Returns complete DER encoded CMS Signed Object
322 * @return DER encoded CMS Signed Object represented as byte array
323 * @exception SMIMEException caused by problems in construction or dealing
324 * with some inner objects instantiated from classes that belong to
325 * org.webdocwf.util.smime.der or org.webdocwf.util.smime.cms packages.
326 */
327 public byte[] getCMSSignedObject() throws SMIMEException {
328 SignedData signData = new SignedData(); // Container for signed data sub object
329
330 signData.addCMSVersion(new CMSVersion(1).getDEREncoded());
331 DigestAlgorithmIdentifiers digAlg = new DigestAlgorithmIdentifiers();
332
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
343 enc.addContentType(cont.getDEREncoded());
344 if (externalSignature == false) {
345 enc.addEncapsulatedContent(message);
346 }
347 signData.addEncapsulatedContentInfo(enc.getDEREncoded());
348 if (countCert != 0)
349 signData.addCertificate(certif.getDEREncoded());
350 signData.addSignerInfos(sInfo.getDEREncoded());
351 Content signedContent = new Content(signData.getDEREncoded(), true); // Filling signed data content in context specific DER object
352 ContentInfo cmsObjectSignedData = new ContentInfo();
353 ContentTypeIdentifier contentTypeSignDataId = new ContentTypeIdentifier("ID_SIGNEDDATA", "NAME_STRING"); // Creating the Content Type
354
355 cmsObjectSignedData.addContentType(contentTypeSignDataId.getDEREncoded());
356 cmsObjectSignedData.addContent(signedContent.getDEREncoded());
357 return cmsObjectSignedData.getDEREncoded();
358 }
359
360 /***
361 * Returns complete DER encoded CMS Signed Object with BASE64 encoding
362 * @return DER encoded CMS Signed Object represented as byte array with
363 * performed BASE64 encoding.
364 * @exception SMIMEException in case of failure in Base64 encoding performed
365 * on the generated SMIME message byte array by method ofMimeAssist class. Also,
366 * it can be caused by problems in construction or work with some inner objects
367 * instantiated from classes that belong to org.webdocwf.util.smime.der or
368 * org.webdocwf.util.smime.cms packages used in getCMSSignedDataSource() method.
369 */
370 public byte[] getBASE64CMSSignedObject() throws SMIMEException {
371 return MimeAssist.getBASE64WithBreakOn76(this.getCMSSignedObject());
372 }
373
374 /***
375 * Returns "micalg" parameter used in Content-Type of external signing
376 * @return String representation of micalg parameter.
377 */
378 private String getMicalg() {
379 int sum = 0;
380
381 if (typeOfDigest[0])
382 sum = sum + 1;
383 if (typeOfDigest[1])
384 sum = sum + 10;
385 if (typeOfDigest[2])
386 sum = sum + 100;
387
388 switch (sum) {
389 case 0:
390 return "\"unknown\"";
391
392 case 1:
393 return "\"sha1\"";
394
395 case 10:
396 return "\"md2\"";
397
398 case 100:
399 return "\"md5\"";
400
401 case 11:
402 return "\"sha1,md2\"";
403
404 case 101:
405 return "\"sha1,md5\"";
406
407 case 110:
408 return "\"md2,md5\"";
409
410 case 111:
411 return "\"sha1,md2,md5\"";
412 }
413 return "\"unknown\"";
414 }
415
416 /***
417 * Returns composed message for external signing.
418 * @return External signed message represented as byte array.
419 * @exception SMIMEException in case of failure in Base64 encoding performed
420 * on the generated SMIME message byte array by method ofMimeAssist class. Also,
421 * it can be caused by problems in construction or work with some inner objects
422 * instantiated from classes that belong to org.webdocwf.util.smime.der or
423 * org.webdocwf.util.smime.cms packages used in getCMSSignedDataSource() method.
424 */
425 private byte[] getExternalSignedMessage() throws SMIMEException {
426
427 try {
428 String extMessage =
429 "--" + boundary + "\r\n" +
430 new String(this.message, "ISO-8859-1") + "\r\n" +
431 "--" + boundary + "\r\n" +
432 "Content-Type: application/x-pkcs7-signature; name=smime.p7s" + "\r\n" +
433 "Content-Transfer-Encoding: base64" + "\r\n" +
434 "Content-Disposition: attachment; filename=smime.p7s" + "\r\n" + "\r\n" +
435 new String(this.getBASE64CMSSignedObject(), "ISO-8859-1") + "\r\n" +
436 "--" + boundary + "--";
437
438 return extMessage.getBytes("ISO-8859-1");
439 } catch (Exception e) {
440 throw SMIMEException.getInstance(this, e, "getExternalSignedMessage");
441 }
442 }
443
444 /***
445 * Implements getContentType method from DataSource interface
446 * @return Content-Type for MIME message header field
447 */
448 public String getContentType() {
449 if (!externalSignature)
450 // For new version of mail clients: "application/pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\""
451 return "application/x-pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\"";
452 else
453 // For new version of mail clients protocol: "application/pkcs7-signature; name=\"smime.p7s\""
454 return "multipart/signed;" + "\r\n" +
455 " protocol=\"application/x-pkcs7-signature\";" + "\r\n" +
456 " micalg=" + this.getMicalg() + "; boundary=" + boundary;
457
458 }
459
460 /***
461 * Implements getInputStream method from DataSource interface
462 * @return CMS signed object
463 * @exception SMIMEIOException thrown as result of SMIMEException
464 */
465 public InputStream getInputStream() throws SMIMEIOException {
466 try {
467 if (!externalSignature)
468 return new ByteArrayInputStream(getCMSSignedObject());
469 else
470 return new ByteArrayInputStream(getExternalSignedMessage());
471 } catch (SMIMEException e) {
472 throw new SMIMEIOException(e);
473 }
474 }
475
476 /***
477 * Implements getName method from DataSource interface
478 * @return Name: SignedDataContentInfo
479 */
480 public String getName() {
481 return "SignedDataContentInfo";
482 }
483
484 /***
485 * Implements getOutputStream method from DataSource interface. This
486 * method is not in use.
487 * @return nothing
488 * @exception IOException is always thrown when this method is used.
489 */
490 public OutputStream getOutputStream() throws IOException {
491 throw new IOException("SignedDataContentInfo does not support getOutputStream()");
492 }
493 }
494
This page was automatically generated by Maven