001// Copyright (C) 2002 IAIK
002// https://sic.tech/
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// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/smime/basic/ProcessMessageDemo.java 20    12.02.25 17:58 Dbratko $
029// $Revision: 20 $
030//
031
032package demo.smime.basic;
033
034import iaik.asn1.structures.AlgorithmID;
035import iaik.cms.CMSAlgorithmID;
036import iaik.smime.CompressedContent;
037import iaik.smime.EncryptedContent;
038import iaik.smime.SMimeBodyPart;
039import iaik.smime.SMimeMultipart;
040import iaik.smime.SMimeParameters;
041import iaik.smime.SignedContent;
042import iaik.x509.X509Certificate;
043
044import java.io.ByteArrayInputStream;
045import java.io.ByteArrayOutputStream;
046import java.io.IOException;
047import java.security.NoSuchAlgorithmException;
048import java.security.PrivateKey;
049import java.security.interfaces.RSAPrivateKey;
050import java.util.Date;
051
052import jakarta.activation.DataHandler;
053import jakarta.activation.FileDataSource;
054import jakarta.mail.Message;
055import jakarta.mail.MessagingException;
056import jakarta.mail.Multipart;
057import jakarta.mail.Session;
058import jakarta.mail.internet.InternetAddress;
059import jakarta.mail.internet.MimeBodyPart;
060import jakarta.mail.internet.MimeMessage;
061
062import demo.DemoSMimeUtil;
063import demo.DemoUtil;
064import demo.keystore.CMSKeyStore;
065import demo.smime.DumpMessage;
066
067/**
068 * This class demonstrates the usage of the IAIK S/MIME implementation for 
069 * cryptographically processing (e.g. signing or encrypting) a received
070 * message. Since the message to be processed has a -- already canonicalized --
071 * multipart content, the SMimeMultipart/SMimeBodyPart control can be disabled
072 * either globally for the whole application:
073 * <pre>
074 * SMimeParameters.setCheckForSMimeParts(false);
075 * </pre>
076 * or only for the specific SignedContent object(s) in use:
077 * <pre>
078 * SignedContent sc = ...;
079 * sc.checkForSMimeParts(false);
080 * ...
081 * </pre>
082 * To run this demo the following packages are required:
083 * <ul>
084 *    <li>
085 *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
086 *    </li>
087 *    <li>
088 *       <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>).
089 *    </li>
090 *    <li>
091 *       <a href="https://jakarta.ee/specifications/mail/" target="_blank">Jakarta</a>/<a href="https://eclipse-ee4j.github.io/angus-mail/" target="_blank">Angus</a> Mail
092 *    </li>   
093 *    <li>
094 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
095 *    </li> 
096 * </ul>
097 */
098public class ProcessMessageDemo {
099    
100  // whether to print all generates test messages to System.out
101  final static boolean PRINT_MESSAGES = true;   
102
103  String firstName = "John";
104  String lastName = "SMime";
105  String to = "smimetest@iaik.at";               // email recipient
106  String from = "smimetest@iaik.at";             // email sender
107  String host = "mailhost";                      // name of the mailhost
108
109  X509Certificate[] signerCertificates;          // list of certificates to include in the S/MIME message
110  X509Certificate recipientCertificate;          // certificate of the recipient
111  X509Certificate signerCertificate;             // certificate of the signer/sender
112  X509Certificate encryptionCertOfSigner;        // signer uses different certificate for encryption
113  PrivateKey signerPrivateKey;                   // private key of the signer/sender
114  
115  /**
116   * Default constructor. Reads certificates and keys from the demo keystore.
117   */
118  public ProcessMessageDemo() {
119    
120    System.out.println();
121    System.out.println("******************************************************************************************");
122    System.out.println("*                                 ProcessMessageDemo                                     *");
123    System.out.println("*      (shows how to cryptographically process (sign, verify) an existing message)       *");
124    System.out.println("******************************************************************************************");
125    System.out.println();
126    
127    // get the certificates from the KeyStore
128    signerCertificates = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
129    signerPrivateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
130    signerCertificate = signerCertificates[0];
131
132    // recipient = signer for this test
133    recipientCertificate = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
134    encryptionCertOfSigner = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
135    
136    // we will cryptographically process an already existing (canonicalized)
137    // message and therefore can disable SMimeMultipart/SMimeBodyPart control
138    SMimeParameters.setCheckForSMimeParts(false);
139  }
140  
141  /**
142   * Starts the demo.
143   *
144   * @throws IOException if an I/O related error occurs
145   */
146  public void start() throws IOException {
147
148        // get the default Session
149        Session session = DemoSMimeUtil.getSession();
150
151        try {
152      // Create a demo Multipart
153      MimeBodyPart mbp1 = new SMimeBodyPart();
154      mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
155            // attachment
156      MimeBodyPart attachment = new SMimeBodyPart();
157      attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
158      attachment.setFileName("test.html");
159        
160      Multipart mp = new SMimeMultipart();
161      mp.addBodyPart(mbp1);
162      mp.addBodyPart(attachment);
163      DataHandler multipart = new DataHandler(mp, mp.getContentType());
164
165      Message msg;    // the message to send
166      ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
167      ByteArrayInputStream bais;  // we read from a stream
168
169      // Create the plain test message
170      msg = createPlainMessage(session, multipart);
171      System.out.println("creating plain message...");
172            msg.saveChanges();
173            msg.writeTo(baos);
174            bais = new ByteArrayInputStream(baos.toByteArray());
175            msg = new MimeMessage(session, bais);
176            if (PRINT_MESSAGES) {
177        printMessage(msg);
178      }
179          DumpMessage.dumpMsg(msg);
180      // the plain message to be crytographically processed
181      MimeMessage plainMessage = (MimeMessage)msg;
182          
183            System.out.println("\n\n*****************************************\n\n");
184      
185      // include RFC822 headers of original message
186      boolean includeHeaders = true;
187      // This is an explicitly signed message
188      msg = createSignedMessage(session, plainMessage, false, includeHeaders);
189      System.out.println("creating explicitly signed message...");
190      baos.reset();
191            msg.saveChanges();
192            msg.writeTo(baos);
193            bais = new ByteArrayInputStream(baos.toByteArray());
194            msg = new MimeMessage(session, bais);
195            if (PRINT_MESSAGES) {
196        printMessage(msg);
197      }
198          DumpMessage.dumpMsg(msg);
199   
200      System.out.println("\n\n*****************************************\n\n");
201
202      // This is an implicitly signed message
203      msg = createSignedMessage(session, plainMessage, true, includeHeaders);
204      System.out.println("creating implicitly signed message...");
205            baos.reset();
206            msg.saveChanges();
207            msg.writeTo(baos);
208            bais = new ByteArrayInputStream(baos.toByteArray());
209            msg = new MimeMessage(session, bais);
210            if (PRINT_MESSAGES) {
211        printMessage(msg);
212      }
213      DumpMessage.dumpMsg(msg);
214          
215          System.out.println("\n\n*****************************************\n\n");
216
217      // Now create an encrypted message
218      msg = createEncryptedMessage(session, 
219                                   plainMessage,
220                                   (AlgorithmID)AlgorithmID.aes256_CBC.clone(),
221                                   256, 
222                                   includeHeaders);
223      System.out.println("creating encrypted message [AES]...");
224          baos.reset();
225          msg.saveChanges();
226          msg.writeTo(baos);
227          bais = new ByteArrayInputStream(baos.toByteArray());
228          msg = new MimeMessage(session, bais);
229          if (PRINT_MESSAGES) {
230          printMessage(msg);
231        }
232          DumpMessage.dumpMsg(msg);
233          
234          System.out.println("\n\n*****************************************\n\n");
235
236      // Now create a implicitly signed and encrypted message with attachment
237          System.out.println("creating implicitly signed and encrypted message...");
238      msg = createSignedAndEncryptedMessage(session,
239                                            plainMessage,
240                                            true,
241                                            includeHeaders);
242    
243          baos.reset();
244          msg.saveChanges();
245          msg.writeTo(baos);
246          bais = new ByteArrayInputStream(baos.toByteArray());
247          msg = new MimeMessage(session, bais);
248          if (PRINT_MESSAGES) {
249          printMessage(msg);
250        }
251          DumpMessage.dumpMsg(msg);
252          
253          System.out.println("\n\n*****************************************\n\n");
254
255      // Now create a explicitly signed and encrypted message with attachment
256          System.out.println("creating explicitly signed and encrypted message ...");
257      msg = createSignedAndEncryptedMessage(session, plainMessage, false, includeHeaders);
258      baos.reset();
259          msg.saveChanges();
260          msg.writeTo(baos);
261          bais = new ByteArrayInputStream(baos.toByteArray());
262          msg = new MimeMessage(session, bais);
263          if (PRINT_MESSAGES) {
264        printMessage(msg);
265      }
266          DumpMessage.dumpMsg(msg);
267          
268          System.out.println("\n\n*****************************************\n\n");
269  
270          // compressed message 
271          msg = createCompressedMessage(session, 
272                                    plainMessage, 
273                                    (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(), 
274                                    includeHeaders);
275          System.out.println("creating message with compressed data...");
276          baos.reset();
277          msg.saveChanges();
278          msg.writeTo(baos);
279          bais = new ByteArrayInputStream(baos.toByteArray());
280          msg = new MimeMessage(session, bais);
281          if (PRINT_MESSAGES) {
282        printMessage(msg);
283      }
284          DumpMessage.dumpMsg(msg);
285      
286      
287      // now the same again but do not include RFC822 headers of original message
288      includeHeaders = false;
289      // This is an explicitly signed message
290      msg = createSignedMessage(session, plainMessage, false, includeHeaders);
291      System.out.println("creating explicitly signed message...");
292      baos.reset();
293      msg.saveChanges();
294      msg.writeTo(baos);
295      bais = new ByteArrayInputStream(baos.toByteArray());
296      msg = new MimeMessage(session, bais);
297      if (PRINT_MESSAGES) {
298        printMessage(msg);
299      }
300      DumpMessage.dumpMsg(msg);
301    
302      System.out.println("\n\n*****************************************\n\n");
303
304
305      // This is an implicitly signed message
306      msg = createSignedMessage(session, plainMessage, true, includeHeaders);
307      System.out.println("creating implicitly signed message...");
308      baos.reset();
309      msg.saveChanges();
310      msg.writeTo(baos);
311      bais = new ByteArrayInputStream(baos.toByteArray());
312      msg = new MimeMessage(session, bais);
313      if (PRINT_MESSAGES) {
314        printMessage(msg);
315      }
316      DumpMessage.dumpMsg(msg);
317    
318      System.out.println("\n\n*****************************************\n\n");
319
320      // Now create an encrypted message
321      msg = createEncryptedMessage(session, 
322                                   plainMessage, 
323                                   (AlgorithmID)AlgorithmID.aes256_CBC.clone(),
324                                   256,
325                                   includeHeaders);
326      System.out.println("creating encrypted message [AES]...");
327      baos.reset();
328      msg.saveChanges();
329      msg.writeTo(baos);
330      bais = new ByteArrayInputStream(baos.toByteArray());
331      msg = new MimeMessage(session, bais);
332      if (PRINT_MESSAGES) {
333        printMessage(msg);
334      }
335      DumpMessage.dumpMsg(msg);
336      
337      System.out.println("\n\n*****************************************\n\n");
338
339      // Now create a implicitly signed and encrypted message with attachment
340      System.out.println("creating implicitly signed and encrypted message ...");
341      msg = createSignedAndEncryptedMessage(session, plainMessage, true, includeHeaders);
342      baos.reset();
343      msg.saveChanges();
344      msg.writeTo(baos);
345      bais = new ByteArrayInputStream(baos.toByteArray());
346      msg = new MimeMessage(session, bais);
347      if (PRINT_MESSAGES) {
348        printMessage(msg);
349      }
350      DumpMessage.dumpMsg(msg);
351      
352      System.out.println("\n\n*****************************************\n\n");
353
354      // Now create a explicitly signed and encrypted message with attachment
355      System.out.println("creating explicitly signed and encrypted message...");
356      msg = createSignedAndEncryptedMessage(session, plainMessage, false, includeHeaders);
357      baos.reset();
358      msg.saveChanges();
359      msg.writeTo(baos);
360      bais = new ByteArrayInputStream(baos.toByteArray());
361      msg = new MimeMessage(session, bais);
362      if (PRINT_MESSAGES) {
363        printMessage(msg);
364      }
365      DumpMessage.dumpMsg(msg);
366      
367      System.out.println("\n\n*****************************************\n\n");
368  
369      // compressed message 
370      msg = createCompressedMessage(session, 
371                                    plainMessage,
372                                    (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(),
373                                    includeHeaders);
374      System.out.println("creating message with compressed data...");
375      baos.reset();
376      msg.saveChanges();
377      msg.writeTo(baos);
378      bais = new ByteArrayInputStream(baos.toByteArray());
379      msg = new MimeMessage(session, bais);
380      if (PRINT_MESSAGES) {
381        printMessage(msg);
382      }
383      DumpMessage.dumpMsg(msg);
384  
385    } catch (Exception ex) {
386          ex.printStackTrace();
387          throw new RuntimeException(ex.toString());
388    }
389  }
390  
391  /**
392   * Creates a MIME message container with the given subject for the given session.
393   * 
394   * @param session the mail sesion
395   * @param subject the subject of the message
396   *
397   * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
398   *
399   * @throws MessagingException if the message cannot be created
400   */
401  public Message createMessage(Session session, String subject) throws MessagingException {
402    MimeMessage msg = new MimeMessage(session);
403    msg.setFrom(new InternetAddress(from));
404        msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to, false));
405        msg.setSentDate(new Date());
406    msg.setSubject(subject);
407    return msg;
408  }
409  
410  /**
411   * Creates a simple plain (neither signed nor encrypted) message.
412   *
413   * @param session the mail session
414   * @param dataHandler the content of the message
415   * 
416   * @return the plain message
417   *
418   * @throws MessagingException if an error occurs when creating the message
419   */
420  public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException {
421
422    Message msg = createMessage(session, "IAIK-S/MIME: Plain message");
423    if (dataHandler != null) {
424      msg.setDataHandler(dataHandler);
425    } else {
426      msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n");
427    }
428        return msg;
429  }
430  
431  /**
432   * Creates a signed and encrypted message.
433   *
434   * @param session the mail session
435   * @param message the message to be signed and encrypted
436   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
437   *                 (multipart/signed) signing
438   * @param includeHeaders whether to inlcude the RFC822 headers of the original
439   *                       message   
440   *  
441   * @return the signed and encrypted message
442   *
443   * @throws MessagingException if an error occurs when creating the message
444   */
445  public Message createSignedAndEncryptedMessage(Session session, 
446                                                 MimeMessage message, 
447                                                 boolean implicit,
448                                                 boolean includeHeaders)
449    throws MessagingException {
450
451    String subject = null;
452    String text = null;
453    if (implicit) {
454      subject = "IAIK-S/MIME: Implicitly Signed and Encrypted";
455      text = "This message is implicitly signed and encrypted!\n\n\n";
456    } else {
457      subject = "IAIK-S/MIME: Explicitly Signed and Encrypted";
458      text = "This message is explicitly signed and encrypted!\n\n\n";
459    }
460    Message msg = createMessage(session, subject);
461
462    SignedContent sc = new SignedContent(implicit);
463    // set the message content
464    if (includeHeaders) {
465      sc.setContent(message, message.getContentType());
466    } else {
467      sc.setDataHandler(message.getDataHandler());
468    }
469    sc.setCertificates(signerCertificates);
470    try {
471      sc.addSigner((RSAPrivateKey)signerPrivateKey, signerCertificate);
472    } catch (NoSuchAlgorithmException ex) {
473      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
474    }
475
476    EncryptedContent ec = new EncryptedContent(sc);
477    // encrypt for the recipient
478    ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
479    // I want to be able to decrypt the message, too
480    ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
481    // set the encryption algorithm
482    try {
483      ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
484    } catch (NoSuchAlgorithmException ex) {
485      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
486    }   
487    msg.setContent(ec, ec.getContentType());
488    // let the EncryptedContent update some message headers
489    ec.setHeaders(msg);
490
491    return msg;
492  }
493  
494  /**
495   * Creates a signed message.
496   *
497   * @param session the mail session
498   * @param message the message to be signed
499   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
500   *                 (multipart/signed) signing
501   * @param includeHeaders whether to inlcude the RFC822 headers of the original
502   *                       message
503   * 
504   * @return the signed message
505   *
506   * @throws MessagingException if an error occurs when creating the message
507   */
508  public Message createSignedMessage(Session session, 
509                                     MimeMessage message,
510                                     boolean implicit,
511                                     boolean includeHeaders)
512    throws Exception {
513
514    String subject = null;
515    
516    if (implicit) {
517      subject = "IAIK-S/MIME: Implicitly Signed";
518    } else {
519      subject = "IAIK-S/MIME: Explicitly Signed";
520    }
521    
522    Message msg = createMessage(session, subject);
523
524    SignedContent sc = new SignedContent(implicit);
525    // set message content
526    if (includeHeaders) {
527      sc.setContent(message, message.getContentType());
528    } else {
529      sc.setDataHandler(message.getDataHandler());
530    }
531    sc.setCertificates(signerCertificates);
532
533    try {
534      sc.addSigner((RSAPrivateKey)signerPrivateKey, signerCertificate);
535    } catch (NoSuchAlgorithmException ex) {
536      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
537    }
538
539    msg.setContent(sc, sc.getContentType());
540    // let the SignedContent update some message headers
541    sc.setHeaders(msg);
542    return msg;
543  }
544  
545  /**
546   * Creates an encrypted message.
547   *
548   * @param session the mail session
549   * @param message the message to be encrypted
550   * @param algorithm the content encryption algorithm to be used
551   * @param keyLength the length of the secret content encryption key to be created and used
552   * @param includeHeaders whether to inlcude the RFC822 headers of the original
553   *                       message 
554   * 
555   * @return the encrypted message
556   *
557   * @throws MessagingException if an error occurs when creating the message
558   */
559  public Message createEncryptedMessage(Session session, 
560                                        MimeMessage message, 
561                                        AlgorithmID algorithm, 
562                                        int keyLength,
563                                        boolean includeHeaders)
564    throws MessagingException {
565
566    StringBuffer subject = new StringBuffer();
567    subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName());
568    if (keyLength > 0) {
569      subject.append("/"+keyLength);
570    }  
571    subject.append("]");
572    Message msg = createMessage(session, subject.toString());
573
574    EncryptedContent ec = new EncryptedContent();
575    // set message content
576    if (includeHeaders) {
577      ec.setContent(message, message.getContentType());
578    } else {
579      ec.setDataHandler(message.getDataHandler());
580    }
581    
582    // encrypt for the recipient
583    ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
584    // I want to be able to decrypt the message, too
585    ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
586    try {
587      ec.setEncryptionAlgorithm(algorithm, keyLength);
588    } catch (NoSuchAlgorithmException ex) {
589      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
590    }   
591
592    msg.setContent(ec, ec.getContentType());
593    // let the EncryptedContent update some message headers
594    ec.setHeaders(msg);
595
596    return msg;
597  }
598  
599    
600  /**
601   * Creates a compressed message.
602   *
603   * @param session the mail session
604   * @param message the message to be compressed
605   * @param algorithm the compression algorithm to be used
606   * @param includeHeaders whether to inlcude the RFC822 headers of the original
607   *                       message   
608   *  
609   * @return the compressed message
610   *
611   * @throws MessagingException if an error occurs when creating the message
612   */
613  public Message createCompressedMessage(Session session, 
614                                         MimeMessage message,
615                                         AlgorithmID algorithm,
616                                         boolean includeHeaders)
617    throws MessagingException {
618
619    String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]";
620    Message msg = createMessage(session, subject.toString());
621
622    CompressedContent compressedContent = new CompressedContent();
623    // set message content
624    if (includeHeaders) {
625      compressedContent.setContent(message, message.getContentType());
626    } else {
627      compressedContent.setDataHandler(message.getDataHandler());
628    }
629    
630    try {
631      compressedContent.setCompressionAlgorithm(algorithm);
632    } catch (NoSuchAlgorithmException ex) {
633      throw new MessagingException("Compression algorithm not supported: " + ex.getMessage());   
634    }   
635
636    msg.setContent(compressedContent, compressedContent.getContentType());
637    // let the CompressedContent update some message headers
638    compressedContent.setHeaders(msg);
639
640    return msg;
641  }
642  
643    
644  
645  /** 
646   * Prints a dump of the given message to System.out.
647   *
648   * @param msg the message to be dumped to System.out
649   */
650  private static void printMessage(Message msg) throws IOException {
651    System.out.println("------------------------------------------------------------------");
652    System.out.println("Message dump: \n");
653    try {
654      msg.writeTo(System.out);
655    } catch (MessagingException ex) {
656      throw new IOException(ex.getMessage());   
657    }    
658    System.out.println("\n------------------------------------------------------------------");
659  }  
660
661
662  /**
663   * The main method.
664   */
665  public static void main(String[] argv) throws IOException {
666
667    DemoSMimeUtil.initDemos();
668        (new ProcessMessageDemo()).start();
669    System.out.println("\nReady!");
670    DemoUtil.waitKey();
671  }
672}