001 // Copyright (C) 2002 IAIK
002 // https://jce.iaik.tugraz.at
003 //
004 // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005 // Communication Technologies SIC
006 // https://sic.tech
007 //
008 // All rights reserved.
009 //
010 // Redistribution and use in source and binary forms, with or without
011 // modification, are permitted provided that the following conditions
012 // are met:
013 // 1. Redistributions of source code must retain the above copyright
014 // notice, this list of conditions and the following disclaimer.
015 // 2. Redistributions in binary form must reproduce the above copyright
016 // notice, this list of conditions and the following disclaimer in the
017 // documentation and/or other materials provided with the distribution.
018 //
019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029 // SUCH DAMAGE.
030
031 // Copyright (C) 2002 IAIK
032 // https://sic.tech/
033 //
034 // Copyright (C) 2003 - 2025 Stiftung Secure Information and
035 // Communication Technologies SIC
036 // https://sic.tech/
037 //
038 // All rights reserved.
039 //
040 // This source is provided for inspection purposes and recompilation only,
041 // unless specified differently in a contract with IAIK. This source has to
042 // be kept in strict confidence and must not be disclosed to any third party
043 // under any circumstances. Redistribution in source and binary forms, with
044 // or without modification, are <not> permitted in any case!
045 //
046 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056 // SUCH DAMAGE.
057 //
058 // $Header: /IAIK-CMS/current/src/demo/smime/big/BigSMimeMailDemo.java 23 12.02.25 17:59 Dbratko $
059 // $Revision: 23 $
060 //
061
062 package demo.smime.big;
063
064 import java.io.BufferedInputStream;
065 import java.io.BufferedOutputStream;
066 import java.io.File;
067 import java.io.FileInputStream;
068 import java.io.FileOutputStream;
069 import java.io.IOException;
070 import java.io.InputStream;
071 import java.io.OutputStream;
072 import java.security.NoSuchAlgorithmException;
073 import java.security.PrivateKey;
074 import java.security.SignatureException;
075 import java.security.interfaces.RSAPrivateKey;
076 import java.util.Date;
077 import java.util.Random;
078
079 import javax.activation.DataHandler;
080 import javax.activation.FileDataSource;
081 import javax.mail.Message;
082 import javax.mail.MessagingException;
083 import javax.mail.Part;
084 import javax.mail.Session;
085 import javax.mail.internet.InternetAddress;
086 import javax.mail.internet.MimeMessage;
087
088 import demo.DemoSMimeUtil;
089 import demo.DemoUtil;
090 import demo.keystore.CMSKeyStore;
091 import demo.smime.DumpMessage;
092 import iaik.asn1.structures.AlgorithmID;
093 import iaik.cms.CMSAlgorithmID;
094 import iaik.smime.AuthEncryptedContent;
095 import iaik.smime.CompressedContent;
096 import iaik.smime.EncryptedContent;
097 import iaik.smime.SMimeParameters;
098 import iaik.smime.SharedFileInputStream;
099 import iaik.smime.SignedContent;
100 import iaik.utils.CryptoUtils;
101 import iaik.utils.Util;
102 import iaik.x509.X509Certificate;
103
104 /**
105 * This class demonstrates the usage of the IAIK S/MIME implementation for
106 * handling S/MIME message with big content data.
107 * <p>
108 * The only difference to the common usage of this S/MIME library is that
109 * this demo uses a temporary file directory to which the content of the
110 * big messages is written during processing. The temporary directory is
111 * created by calling method {@link iaik.smime.SMimeParameters#setTempDirectory(String, int)
112 * SMimeParameters#setTempDirectory}:
113 * <pre>
114 * int bufSize = 16348;
115 * String tmpDir = ...;
116 * SMimeParameters.setTempDirectory(tmpDir, bufSize);
117 * </pre>
118 * See Javadoc of {@link iaik.smime.SMimeParameters#setTempDirectory(String, int)
119 * SMimeParameters#setTempDirectory} for usage information.
120 * <p>
121 * To run this demo the following packages are required:
122 * <ul>
123 * <li>
124 * <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
125 * </li>
126 * <li>
127 * <code>iaik_jce(_full).jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">IAIK-JCE Core Crypto Library</a>).
128 * </li>
129 * <li>
130 * <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
131 * </li>
132 * <li>
133 * <code>activation.jar</code> (<a href="http://www.oracle.com/technetwork/java/javase/downloads/index-135046.html" target="_blank">Java Activation Framework</a>; required for JDK versions < 1.6).
134 * </li>
135 * </ul>
136 * The data for this demo is randomly created and stored into a file which is
137 * deleted again at the end of this demo. Note that running this demo may take
138 * some certain time because it processes some MB of data.
139 */
140 public class BigSMimeMailDemo {
141
142 // The directory where to write mails.
143 final static String TEST_DIR = "test";
144
145 // The name of the data file.
146 final static String DATA_FILE_NAME = TEST_DIR + "/test.dat";
147
148 // The data size (in bytes).
149 final static int DATA_SIZE = 15000 * 1024;
150
151
152 String firstName_ = "John"; // name of sender
153 String lastName_ = "SMime";
154 String from_ = "smimetest@iaik.tugraz.at"; // email sender
155 String to_ = "smimetest@iaik.tugraz.at"; // email recipient
156 String host_ = "mailhost"; // name of the mailhost
157
158 X509Certificate[] signerCertificates_; // list of certificates to include in the S/MIME message
159 X509Certificate recipientCertificate_; // certificate of the recipient
160 PrivateKey recipientKey_; // the private key of the recipient
161 X509Certificate signerCertificate_; // certificate of the signer/sender
162 X509Certificate encryptionCertOfSigner_; // signer uses different certificate for encryption
163 PrivateKey signerPrivateKey_; // private key of the signer/sender
164
165
166 /**
167 * Default constructor. Reads certificates and keys from the demo keystore.
168 */
169 public BigSMimeMailDemo() {
170
171 System.out.println();
172 System.out.println("******************************************************************************************");
173 System.out.println("* BigSMimeMailDemo demo *");
174 System.out.println("* (shows how to create and parse big S/MIME messages) *");
175 System.out.println("******************************************************************************************");
176 System.out.println();
177
178 // get the certificates from the KeyStore
179 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
180 signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
181 signerCertificate_ = signerCertificates_[0];
182
183 // recipient = signer for this test
184 recipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
185 recipientKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
186 encryptionCertOfSigner_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
187 }
188
189 /**
190 * Starts the demo.
191 *
192 * @throws IOException if an I/O related error occurs
193 */
194 public void start() throws IOException {
195
196 // first we create the big test data file (may take some time
197 createDataFile(DATA_FILE_NAME, DATA_SIZE);
198
199 // get the default Session
200 Session session = DemoSMimeUtil.getSession();
201
202 // the name of the file holding the test message
203 String fileName;
204 // the file holding the test message
205 File file;
206 // the stream to which to write the mail to a file
207 FileOutputStream fos = null;
208 // the stream from which to read the mail from a file
209 SharedFileInputStream fis = null;
210
211 // we specify a temp directory to which temporary message contents
212 // shall be written
213 int bufSize = 64 * 1024;
214 SMimeParameters.setTempDirectory(TEST_DIR, bufSize);
215
216 try {
217
218 // DataHandler for reading the data
219 DataHandler dataHandler = new DataHandler(new FileDataSource(DATA_FILE_NAME));
220
221 Message msg; // the message to send
222
223 // 1. Explicitly signed message
224 System.out.println("Creating explicitly signed message...");
225 // create
226 msg = createSignedMessage(session, dataHandler, false);
227 msg.saveChanges();
228 fileName = TEST_DIR + "/explicitSigned.eml";
229 fos = new FileOutputStream(fileName);
230 System.out.println("Writing explicitly signed message to " + fileName);
231 msg.writeTo(new BufferedOutputStream(fos));
232 fos.close();
233 System.out.println("Explicitly signed message created.");
234 // read
235 file = new File(fileName);
236 fis = new SharedFileInputStream(file);
237 System.out.println("Parsing explicitly signed message from " + fileName);
238 msg = new MimeMessage(session, fis);
239 parse(msg);
240 fis.close();
241 file.delete();
242 System.out.println("\n\n*****************************************\n\n");
243
244
245 // 2. Implicitly signed message
246 msg = createSignedMessage(session, dataHandler, true);
247 System.out.println("creating implicitly signed message...");
248 fileName = TEST_DIR + "/implicitSigned.eml";
249 fos = new FileOutputStream(fileName);
250 System.out.println("Writing implicitly signed message to " + fileName);
251 msg.writeTo(new BufferedOutputStream(fos));
252 fos.close();
253 System.out.println("Implicitly signed message created.");
254 // read
255 file = new File(fileName);
256 fis = new SharedFileInputStream(file);
257 System.out.println("Parsing implicitly signed message from " + fileName);
258 msg = new MimeMessage(session, fis);
259 parse(msg);
260 fis.close();
261 file.delete();
262 System.out.println("\n\n*****************************************\n\n");
263
264 // 3. Encrypted messages (AES)
265 System.out.println("Creating encrypted message [AES/256]...");
266 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, false);
267 fileName = TEST_DIR + "/encrypted.eml";
268 fos = new FileOutputStream(fileName);
269 System.out.println("Writing encrypted message to " + fileName);
270 msg.writeTo(new BufferedOutputStream(fos));
271 fos.close();
272 System.out.println("Encrypted message created.");
273 // read
274 file = new File(fileName);
275 fis = new SharedFileInputStream(file);
276 System.out.println("Parsing encrypted message from " + fileName);
277 msg = new MimeMessage(session, fis);
278 parse(msg);
279 fis.close();
280 file.delete();
281
282 System.out.println("\n\n*****************************************\n\n");
283
284 // 4. Implicitly signed and encrypted message
285 System.out.println("Creating implicitly signed and encrypted message");
286 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, true, false);
287 fileName = TEST_DIR + "/impsigenc.eml";
288 fos = new FileOutputStream(fileName);
289 System.out.println("Writing implicitly signed and encrypted message to " + fileName);
290 msg.writeTo(new BufferedOutputStream(fos));
291 fos.close();
292 System.out.println("Implicitly signed and encrypted message created.");
293 // read
294 file = new File(fileName);
295 fis = new SharedFileInputStream(file);
296 System.out.println("Parsing implicitly signed and encrypted message from " + fileName);
297 msg = new MimeMessage(session, fis);
298 parse(msg);
299 fis.close();
300 file.delete();
301
302 System.out.println("\n\n*****************************************\n\n");
303
304 // 6. Explicitly signed and encrypted message
305 System.out.println("Creating explicitly signed and encrypted message");
306 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, false, false);
307 fileName = TEST_DIR + "/impsigenc.eml";
308 fos = new FileOutputStream(fileName);
309 System.out.println("Writing explicitly signed and encrypted message to " + fileName);
310 msg.writeTo(new BufferedOutputStream(fos));
311 fos.close();
312 System.out.println("Explicitly signed and encrypted message created.");
313 // read
314 file = new File(fileName);
315 fis = new SharedFileInputStream(file);
316 System.out.println("Parsing explicitly signed and encrypted message from " + fileName);
317 msg = new MimeMessage(session, fis);
318 parse(msg);
319 fis.close();
320 file.delete();
321
322 System.out.println("\n\n*****************************************\n\n");
323
324 // 7. compressed message
325 System.out.println("Creating compressed message");
326 msg = createCompressedMessage(session, dataHandler, (AlgorithmID)CMSAlgorithmID.zlib_compress.clone());
327 fileName = TEST_DIR + "/compressed.eml";
328 System.out.println("Writing compressed message to " + fileName);
329 fos = new FileOutputStream(fileName);
330 msg.writeTo(new BufferedOutputStream(fos));
331 fos.close();
332 System.out.println("Compressed message created.");
333 // read
334 file = new File(fileName);
335 fis = new SharedFileInputStream(file);
336 System.out.println("Parsing compressed message from " + fileName);
337 msg = new MimeMessage(session, fis);
338 parse(msg);
339 fis.close();
340 file.delete();
341
342 // 8. Authenticated Encrypted messages (AES)
343 System.out.println("Creating authenticated encrypted message [AES-GCM/256]...");
344 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, true);
345 fileName = TEST_DIR + "/encrypted.eml";
346 fos = new FileOutputStream(fileName);
347 System.out.println("Writing encrypted message to " + fileName);
348 msg.writeTo(new BufferedOutputStream(fos));
349 fos.close();
350 System.out.println("Encrypted message created.");
351 // read
352 file = new File(fileName);
353 fis = new SharedFileInputStream(file);
354 System.out.println("Parsing encrypted message from " + fileName);
355 msg = new MimeMessage(session, fis);
356 parse(msg);
357 fis.close();
358 file.delete();
359
360 System.out.println("\n\n*****************************************\n\n");
361
362
363 // 9. Authenticated Encrypted messages (ChaCha20Poly1305)
364 System.out.println("Creating authenticated encrypted message [ChaCha20Poly1305]...");
365 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, true);
366 fileName = TEST_DIR + "/encrypted.eml";
367 fos = new FileOutputStream(fileName);
368 System.out.println("Writing encrypted message to " + fileName);
369 msg.writeTo(new BufferedOutputStream(fos));
370 fos.close();
371 System.out.println("Encrypted message created.");
372 // read
373 file = new File(fileName);
374 fis = new SharedFileInputStream(file);
375 System.out.println("Parsing encrypted message from " + fileName);
376 msg = new MimeMessage(session, fis);
377 parse(msg);
378 fis.close();
379 file.delete();
380
381 System.out.println("\n\n*****************************************\n\n");
382
383 // 10. Implicitly signed and authenticated encrypted message
384 System.out.println("Creating implicitly signed and authenticated encrypted message (AES)");
385 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, true, true);
386 fileName = TEST_DIR + "/impsigenc.eml";
387 fos = new FileOutputStream(fileName);
388 System.out.println("Writing implicitly signed and encrypted message to " + fileName);
389 msg.writeTo(new BufferedOutputStream(fos));
390 fos.close();
391 System.out.println("Implicitly signed and encrypted message created.");
392 // read
393 file = new File(fileName);
394 fis = new SharedFileInputStream(file);
395 System.out.println("Parsing implicitly signed and encrypted message from " + fileName);
396 msg = new MimeMessage(session, fis);
397 parse(msg);
398 fis.close();
399 file.delete();
400
401 System.out.println("\n\n*****************************************\n\n");
402
403 // 11. Explicitly signed and authenticated encrypted message
404 System.out.println("Creating explicitly authenticated signed and encrypted message (AES)");
405 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, false, true);
406 fileName = TEST_DIR + "/impsigenc.eml";
407 fos = new FileOutputStream(fileName);
408 System.out.println("Writing explicitly signed and encrypted message to " + fileName);
409 msg.writeTo(new BufferedOutputStream(fos));
410 fos.close();
411 System.out.println("Explicitly signed and encrypted message created.");
412 // read
413 file = new File(fileName);
414 fis = new SharedFileInputStream(file);
415 System.out.println("Parsing explicitly signed and encrypted message from " + fileName);
416 msg = new MimeMessage(session, fis);
417 parse(msg);
418 fis.close();
419 file.delete();
420
421 System.out.println("\n\n*****************************************\n\n");
422
423 // 12. Implicitly signed and authenticated encrypted message
424 System.out.println("Creating implicitly signed and authenticated encrypted message (ChaCha20Poly1305)");
425 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, true, true);
426 fileName = TEST_DIR + "/impsigenc.eml";
427 fos = new FileOutputStream(fileName);
428 System.out.println("Writing implicitly signed and encrypted message to " + fileName);
429 msg.writeTo(new BufferedOutputStream(fos));
430 fos.close();
431 System.out.println("Implicitly signed and encrypted message created.");
432 // read
433 file = new File(fileName);
434 fis = new SharedFileInputStream(file);
435 System.out.println("Parsing implicitly signed and encrypted message from " + fileName);
436 msg = new MimeMessage(session, fis);
437 parse(msg);
438 fis.close();
439 file.delete();
440
441 System.out.println("\n\n*****************************************\n\n");
442
443 // 13. Explicitly signed and authenticated encrypted message
444 System.out.println("Creating explicitly authenticated signed and encrypted message (ChaCha20Poly1305)");
445 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, false, true);
446 fileName = TEST_DIR + "/impsigenc.eml";
447 fos = new FileOutputStream(fileName);
448 System.out.println("Writing explicitly signed and encrypted message to " + fileName);
449 msg.writeTo(new BufferedOutputStream(fos));
450 fos.close();
451 System.out.println("Explicitly signed and encrypted message created.");
452 // read
453 file = new File(fileName);
454 fis = new SharedFileInputStream(file);
455 System.out.println("Parsing explicitly signed and encrypted message from " + fileName);
456 msg = new MimeMessage(session, fis);
457 parse(msg);
458 fis.close();
459 file.delete();
460
461 System.out.println("\n\n*****************************************\n\n");
462
463
464 } catch (Exception ex) {
465 if (fos != null) {
466 try {
467 fos.close();
468 } catch (Exception e) {
469 // ignore
470 }
471 }
472 if (fis != null) {
473 try {
474 fis.close();
475 } catch (Exception e) {
476 // ignore
477 }
478 }
479 ex.printStackTrace();
480 throw new RuntimeException(ex.toString());
481
482 } finally {
483 // try to delete temporary directory
484 SMimeParameters.deleteTempDirectory();
485 SMimeParameters.setTempDirectory(null, -1);
486 // delete data file
487 File dataFile = new File(DATA_FILE_NAME);
488 dataFile.delete();
489 }
490
491 }
492
493
494 /**
495 * Creates a MIME message container with the given subject for the given session.
496 *
497 * @param session the mail sesion
498 * @param subject the subject of the message
499 *
500 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
501 *
502 * @throws MessagingException if the message cannot be created
503 */
504 public Message createMessage(Session session, String subject) throws MessagingException {
505 MimeMessage msg = new MimeMessage(session);
506 msg.setFrom(new InternetAddress(from_));
507 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to_, false));
508 msg.setSentDate(new Date());
509 msg.setSubject(subject);
510 return msg;
511 }
512
513
514 /**
515 * Creates a signed and encrypted message.
516 *
517 * @param session the mail session
518 * @param dataHandler the content of the message to be signed and encrypted
519 * @param algorithm the content encryption algorithm to be used
520 * @param keyLength the length of the secret content encryption key to be created and used
521 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
522 * (multipart/signed) signing
523 * @param authEncrypt whether to create an authenticated encrypted or an encrypted message
524 *
525 * @return the signed and encrypted message
526 *
527 * @throws MessagingException if an error occurs when creating the message
528 */
529 public Message createSignedAndEncryptedMessage(Session session,
530 DataHandler dataHandler,
531 AlgorithmID algorithm,
532 int keyLength,
533 boolean implicit,
534 boolean authEncrypt)
535 throws MessagingException {
536
537 String subject = null;
538 if (implicit) {
539 subject = "IAIK-S/MIME: Implicitly Signed and Encrypted";
540 } else {
541 subject = "IAIK-S/MIME: Explicitly Signed and Encrypted";
542 }
543 Message msg = createMessage(session, subject);
544
545 SignedContent sc = new SignedContent(implicit);
546 sc.setDataHandler(dataHandler);
547 sc.setCertificates(signerCertificates_);
548 try {
549 sc.addSigner((RSAPrivateKey)signerPrivateKey_, signerCertificate_);
550 } catch (NoSuchAlgorithmException ex) {
551 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
552 }
553
554 EncryptedContent ec = authEncrypt ? new AuthEncryptedContent(sc) : new EncryptedContent(sc);
555 // encrypt for the recipient
556 ec.addRecipient(recipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
557 // I want to be able to decrypt the message, too
558 ec.addRecipient(encryptionCertOfSigner_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
559 // set the encryption algorithm
560 try {
561 ec.setEncryptionAlgorithm((AlgorithmID)algorithm.clone(), keyLength);
562 } catch (NoSuchAlgorithmException ex) {
563 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());
564 }
565 msg.setContent(ec, ec.getContentType());
566 // let the EncryptedContent update some message headers
567 ec.setHeaders(msg);
568
569 return msg;
570 }
571
572 /**
573 * Creates a signed message.
574 *
575 * @param session the mail session
576 * @param dataHandler the content of the message to be signed
577 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
578 * (multipart/signed) signing
579 *
580 * @return the signed message
581 *
582 * @throws MessagingException if an error occurs when creating the message
583 */
584 public Message createSignedMessage(Session session, DataHandler dataHandler, boolean implicit)
585 throws MessagingException {
586
587 String subject = null;
588 StringBuffer buf = new StringBuffer();
589
590 if (implicit) {
591 subject = "IAIK-S/MIME: Implicitly Signed";
592 buf.append("This message is implicitly signed!\n");
593 buf.append("You need an S/MIME aware mail client to view this message.\n");
594 buf.append("\n\n");
595 } else {
596 subject = "IAIK-S/MIME: Explicitly Signed";
597 buf.append("This message is explicitly signed!\n");
598 buf.append("Every mail client can view this message.\n");
599 buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
600 buf.append("\n\n");
601 }
602
603 Message msg = createMessage(session, subject);
604
605 SignedContent sc = new SignedContent(implicit);
606
607 if (dataHandler != null) {
608 sc.setDataHandler(dataHandler);
609 } else {
610 sc.setText(buf.toString());
611 }
612 sc.setCertificates(signerCertificates_);
613
614 try {
615 sc.addSigner((RSAPrivateKey)signerPrivateKey_, signerCertificate_);
616 } catch (NoSuchAlgorithmException ex) {
617 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
618 }
619
620 msg.setContent(sc, sc.getContentType());
621 // let the SignedContent update some message headers
622 sc.setHeaders(msg);
623 return msg;
624 }
625
626 /**
627 * Creates an encrypted message.
628 *
629 * @param session the mail session
630 * @param dataHandler the dataHandler providing the content to be encrypted
631 * @param algorithm the content encryption algorithm to be used
632 * @param keyLength the length of the secret content encryption key to be created and used
633 * @param authEncrypt whether to create an authenticated encrypted or an encrypted message
634 *
635 * @return the encrypted message
636 *
637 * @throws MessagingException if an error occurs when creating the message
638 */
639 public Message createEncryptedMessage(Session session,
640 DataHandler dataHandler,
641 AlgorithmID algorithm,
642 int keyLength,
643 boolean authEncrypt)
644 throws MessagingException {
645
646 StringBuffer subject = new StringBuffer();
647 subject.append("IAIK-S/MIME: " + (authEncrypt ? "Authenticated " : "") + "Encrypted ["+algorithm.getName());
648 if (keyLength > 0) {
649 subject.append("/"+keyLength);
650 }
651 subject.append("]");
652 Message msg = createMessage(session, subject.toString());
653
654 EncryptedContent ec = authEncrypt ? new AuthEncryptedContent() : new EncryptedContent();
655
656
657 ec.setDataHandler(dataHandler);
658 // encrypt for the recipient
659 ec.addRecipient(recipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
660 // I want to be able to decrypt the message, too
661 ec.addRecipient(encryptionCertOfSigner_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
662 try {
663 ec.setEncryptionAlgorithm(algorithm, keyLength);
664 } catch (NoSuchAlgorithmException ex) {
665 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());
666 }
667
668 msg.setContent(ec, ec.getContentType());
669 // let the EncryptedContent update some message headers
670 ec.setHeaders(msg);
671
672 return msg;
673 }
674
675 /**
676 * Creates a compressed message.
677 *
678 * @param session the mail session
679 * @param dataHandler the datahandler supplying the content to be compressed
680 * @param algorithm the compression algorithm to be used
681 *
682 * @return the compressed message
683 *
684 * @throws MessagingException if an error occurs when creating the message
685 */
686 public Message createCompressedMessage(Session session, DataHandler dataHandler, AlgorithmID algorithm)
687 throws MessagingException {
688
689 String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]";
690 Message msg = createMessage(session, subject.toString());
691
692 CompressedContent compressedContent = new CompressedContent();
693 compressedContent.setDataHandler(dataHandler);
694
695 try {
696 compressedContent.setCompressionAlgorithm(algorithm);
697 } catch (NoSuchAlgorithmException ex) {
698 throw new MessagingException("Compression algorithm not supported: " + ex.getMessage());
699 }
700
701 msg.setContent(compressedContent, compressedContent.getContentType());
702 // let the CompressedContent update some message headers
703 compressedContent.setHeaders(msg);
704
705 return msg;
706 }
707
708 /**
709 * Parses the given object (message, part, ... ).
710 *
711 * @param o the object (message, part, ... ) to be parsed
712 *
713 * @throws Exception if an exception occurs while parsing
714 */
715 public void parse(Object o) throws Exception {
716 DumpMessage dumpMsg = new DumpMessage();
717 if (o instanceof Message) {
718 dumpMsg.dumpEnvelope((Message)o);
719 }
720 if (o instanceof Part) {
721 System.out.println("CONTENT-TYPE: "+((Part)o).getContentType());
722 o = ((Part)o).getContent();
723 }
724
725 if (o instanceof EncryptedContent) {
726
727 // encrypted
728 System.out.println("This message is encrypted!");
729
730 EncryptedContent ec = (EncryptedContent)o;
731 ec.decryptSymmetricKey(recipientKey_, recipientCertificate_);
732 parse(ec.getContent());
733
734 } else if (o instanceof SignedContent) {
735
736 // signed
737 System.out.println("This message is signed!");
738
739 SignedContent sc = (SignedContent)o;
740
741 X509Certificate signer = null;
742 try {
743 signer = sc.verify();
744 System.out.println("This message is signed from: "+signer.getSubjectDN());
745 } catch (SignatureException ex) {
746 throw new SignatureException("Signature verification error: " + ex.toString());
747 }
748
749 parse(sc.getContent());
750
751 } else if (o instanceof CompressedContent) {
752
753 System.out.println("The content of this message is compressed.");
754 CompressedContent compressed = (CompressedContent)o;
755 parse(compressed.getContent());
756
757 } else if (o instanceof InputStream) {
758
759 // we already know that the content is an input stream (thus we must not check for other content values)
760 System.out.println("Content is just an input stream.");
761 System.out.println("---------------------------");
762 InputStream is = (InputStream)o;
763 // read content
764 if (readContent(is) == false) {
765 throw new Exception("Content not equal to original one!");
766 }
767
768 } else {
769 throw new Exception("Unexpected object!");
770 }
771 }
772
773
774 /**
775 * Reads the content from the given input stream, writes it
776 * to a temp file and then compares the tmp file with the
777 * original data file.
778 *
779 * @param content the content to be written
780 *
781 * @return <code>true</code> if the content is equal to the original one,
782 * <code>false</code> if it differs from the original one
783 * @throws IOException
784 */
785 private final static boolean readContent(InputStream content) throws IOException {
786 String fileName = TEST_DIR + "/tmp.dat";
787
788 // file stream to which to write content
789 FileOutputStream fos = null;
790 // file stream from which to read content for comparison
791 FileInputStream fis = null;
792 // temp file to which write / from which read content for comparison
793 File file = null;
794 // file stream from which to read original content for comparison
795 FileInputStream origFis = null;
796 try {
797 // write to file
798 System.out.println("Write content to " + fileName);
799 fos = new FileOutputStream(fileName);
800 InputStream in = new BufferedInputStream(content);
801 OutputStream out = new BufferedOutputStream(fos);
802 Util.copyStream(in,
803 out,
804 new byte[8192]);
805 out.flush();
806 fos.close();
807 fos = null;
808
809 // compare contents
810 file = new File(fileName);
811 fis = new FileInputStream(file);
812 origFis = new FileInputStream(DATA_FILE_NAME);
813 System.out.println("Compare with original content");
814
815 boolean equal = equals(new BufferedInputStream(fis), new BufferedInputStream(origFis));
816 if (equal) {
817 System.out.println("Content ok");
818 }
819 return equal;
820 } finally {
821 if (origFis != null) {
822 try {
823 origFis.close();
824 } catch (IOException ex) {
825 // ignore
826 }
827 }
828 if (fis != null) {
829 try {
830 fis.close();
831 } catch (IOException ex) {
832 // ignore
833 }
834 // delete tmp file
835 try {
836 file.delete();
837 } catch (Exception ex) {
838 // ignore
839 }
840 }
841 if (fos != null) {
842 try {
843 fos.close();
844 } catch (IOException ex) {
845 // ignore
846 }
847 }
848
849 }
850 }
851
852
853
854
855 /**
856 * Creates a file of the given size and fills it with random
857 * data. The file is written to the directory used by this
858 * demo. If the directory does not exist it is created.
859 *
860 * @param fileName the name of the file to be created
861 * @param size the size (in bytes) of the file
862 *
863 * @throws IOException if an exception occurs during creating/writing the
864 * file
865 */
866 private final static void createDataFile(String fileName, int size)
867 throws IOException {
868 // create output directory
869 File dir = new File(TEST_DIR);
870 if (dir.exists() == false) {
871 dir.mkdir();
872 }
873 // create big data file
874 System.out.println("Creating " + size + " b data file " + fileName);
875 OutputStream os = null;
876 try {
877 Random random = new Random();
878 os = new BufferedOutputStream(new FileOutputStream(fileName));
879 int bufSize = 8192;
880 byte[] buf = new byte[bufSize];
881 int blockSize = size / bufSize;
882 for (int i = 0; i < blockSize; i++) {
883 random.nextBytes(buf);
884 os.write(buf);
885 os.flush();
886 }
887 // write the rest
888 buf = new byte[size - blockSize * bufSize];
889 random.nextBytes(buf);
890 os.write(buf);
891 os.flush();
892 System.out.println("Data file " + fileName + " created.");
893 } finally {
894 if (os != null) {
895 try {
896 os.close();
897 } catch (IOException ex) {
898 // ignore
899 }
900 }
901 }
902 }
903
904 /**
905 * Compares the data read from the two input streams.
906 *
907 * @param source1 the first input stream
908 * @param source2 the second input stream
909 *
910 * @return true if the data is equal, false if it is not equal
911 *
912 * @throws IOException if an exception occurs
913 */
914 private final static boolean equals(InputStream source1, InputStream source2)
915 throws IOException
916 {
917
918 boolean equals;
919 int bufSize = 8192;
920 if (source1 != source2) {
921 if ((source1 != null) && (source2 != null)) {
922 byte[] buffer1 = new byte[bufSize];
923 byte[] buffer2 = new byte[bufSize];
924 if (buffer1.length > buffer2.length) {
925 // swap
926 byte[] temp = buffer1;
927 buffer1 = buffer2;
928 buffer2 = temp;
929 }
930
931 equals = true;
932 int bytesRead1;
933 while ((bytesRead1 = source1.read(buffer1)) >= 0) {
934 int bytesRead2;
935 int totalBytesRead2 = 0;
936 while (((bytesRead2 = source2.read(buffer2, totalBytesRead2, (bytesRead1 - totalBytesRead2))) >= 0)
937 && (totalBytesRead2 < bytesRead1)) {
938 totalBytesRead2 += bytesRead2;
939 }
940 if (totalBytesRead2 == bytesRead1) {
941 if (!CryptoUtils.equalsBlock(buffer1, 0, buffer2, 0, bytesRead1)) {
942 equals = false;
943 break;
944 }
945 } else {
946 equals = false;
947 break;
948 }
949 }
950 if (source2.read(buffer2) >= 0) {
951 // there has been data left in stream 2
952 equals = false;
953 }
954 } else {
955 equals = false;
956 }
957 } else {
958 equals = true;
959 }
960
961 return equals ;
962 }
963
964
965
966 /**
967 * The main method.
968 */
969 public static void main(String[] argv) throws IOException {
970
971 DemoSMimeUtil.initDemos();
972 (new BigSMimeMailDemo()).start();
973 System.out.println("\nReady!");
974 DemoUtil.waitKey();
975 }
976 }