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/SMimeV4Demo.java 8     12.02.25 17:59 Dbratko $
029// $Revision: 8 $
030//
031
032package demo.smime.basic;
033
034import java.io.ByteArrayInputStream;
035import java.io.ByteArrayOutputStream;
036import java.io.IOException;
037import java.security.NoSuchAlgorithmException;
038import java.security.PrivateKey;
039import java.util.Date;
040
041import jakarta.activation.DataHandler;
042import jakarta.activation.FileDataSource;
043import jakarta.mail.Message;
044import jakarta.mail.MessagingException;
045import jakarta.mail.Multipart;
046import jakarta.mail.Session;
047import jakarta.mail.internet.InternetAddress;
048import jakarta.mail.internet.MimeBodyPart;
049import jakarta.mail.internet.MimeMessage;
050import jakarta.mail.internet.MimeMultipart;
051
052import demo.DemoSMimeUtil;
053import demo.DemoUtil;
054import demo.keystore.CMSKeyStore;
055import demo.smime.DumpMessage;
056import iaik.asn1.ObjectID;
057import iaik.asn1.structures.AlgorithmID;
058import iaik.asn1.structures.Name;
059import iaik.cms.CMSAlgorithmID;
060import iaik.pkcs.PKCSException;
061import iaik.pkcs.pkcs10.CertificateRequest;
062import iaik.smime.AuthEncryptedContent;
063import iaik.smime.CompressedContent;
064import iaik.smime.EncryptedContent;
065import iaik.smime.PKCS10Content;
066import iaik.smime.SMimeBodyPart;
067import iaik.smime.SMimeMultipart;
068import iaik.smime.SMimeParameters;
069import iaik.smime.SignedContent;
070import iaik.x509.X509Certificate;
071
072/**
073 * This class demonstrates the usage of the IAIK S/MIME implementation. It shows how to create
074 * signed and/or encrypted S/MIMEv4 messages and how to parse them and verify the signatures 
075 * and decrypt the content, respectively.
076 * <p>
077 * To run this demo the following packages are required:
078 * <ul>
079 *    <li>
080 *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
081 *    </li>
082 *    <li>
083 *       <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>).
084 *    </li>
085 *    <li>
086 *       <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
087 *    </li>   
088 *    <li>
089 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
090 *    </li> 
091 * </ul>
092 * You also will need the IAIK-JCE library, <code>iaik_jce(_full).jar</code>.
093 * <p>
094 * This demo uses RSA both for signing an encryption. Look at the demos in package <code>demo.smime.ecc</code> for
095 * examples based on elliptic curve cryptography.
096 * 
097 */
098public class SMimeV4Demo {
099    
100  // whether to print dump all generates test messages to System.out
101  final static boolean PRINT_MESSAGES = false;   
102  
103
104  String firstName_ = "John";
105  String lastName_ = "SMime";
106  String to_ = "smimetest@iaik.tugraz.at";     // email recipient
107  String from_ = "smimetest@iaik.tugraz.at";   // email sender
108  String host_ = "mailhost";                   // name of the mailhost
109
110  X509Certificate[] signerCertificates_;       // list of certificates to include in the S/MIME message
111  X509Certificate signerCertificate_;          // certificate of the signer/sender
112  PrivateKey signerPrivateKey_;                // private key of the signer/sender
113  
114  X509Certificate senderCryptCertificate_;     // the encryption certificate of the signer/sender
115  
116  X509Certificate rsaRecipientCertificate_;    // RSA encryption cert and key of a recipient
117  
118  /**
119   * Default constructor. Reads certificates and keys from the demo keystore.
120   */
121  public SMimeV4Demo() {
122    
123    System.out.println();
124    System.out.println("********************************************************************************************");
125    System.out.println("*                                SMimeV4Demo demo                                          *");
126    System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIMEv4 messages) *");
127    System.out.println("********************************************************************************************");
128    System.out.println();
129    
130    // get the certificates from the KeyStore
131    signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
132    signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
133    signerCertificate_ = signerCertificates_[0];
134    senderCryptCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
135
136    rsaRecipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
137
138  }
139  
140  /**
141   * Starts the demo.
142   *
143   * @throws IOException if an I/O related error occurs
144   */
145  public void start() throws IOException {
146
147    // get the default Session
148        Session session = DemoSMimeUtil.getSession();
149
150        try {
151      // Create a demo Multipart
152      MimeBodyPart mbp1 = new SMimeBodyPart();
153          mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
154          // attachment
155      MimeBodyPart attachment = new SMimeBodyPart();
156      attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
157      attachment.setFileName("test.html");
158
159      Multipart mp = new SMimeMultipart();
160      mp.addBodyPart(mbp1);
161      mp.addBodyPart(attachment);
162      DataHandler multipart = new DataHandler(mp, mp.getContentType());
163
164      Message msg;    // the message to send
165      ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
166      ByteArrayInputStream bais;  // we read from a stream
167
168      // This is a plain message
169      msg = createPlainMessage(session, multipart);
170      System.out.println("creating plain message...");
171          msg.saveChanges();
172          msg.writeTo(baos);
173          bais = new ByteArrayInputStream(baos.toByteArray());
174          msg = new MimeMessage(session, bais);
175          if (PRINT_MESSAGES) {
176        printMessage(msg);
177      }
178          DumpMessage.dumpMsg(msg);
179          
180          System.out.println("\n\n*****************************************\n\n");
181
182      // This is an explicitly signed message
183      msg = createSignedMessage(session, multipart, false, AlgorithmID.sha256, AlgorithmID.rsaEncryption);
184      System.out.println("creating explicitly signed message...");
185      baos.reset();
186          msg.saveChanges();
187          msg.writeTo(baos);
188          bais = new ByteArrayInputStream(baos.toByteArray());
189          msg = new MimeMessage(session, bais);
190          if (PRINT_MESSAGES) {
191        printMessage(msg);
192      }
193          DumpMessage.dumpMsg(msg);
194          
195          System.out.println("\n\n*****************************************\n\n");
196
197
198      // This is an implicitly signed message
199      msg = createSignedMessage(session, multipart, true, AlgorithmID.sha256, AlgorithmID.rsaEncryption);
200      System.out.println("creating implicitly signed message...");
201          baos.reset();
202          msg.saveChanges();
203          msg.writeTo(baos);
204          bais = new ByteArrayInputStream(baos.toByteArray());
205          msg = new MimeMessage(session, bais);
206          if (PRINT_MESSAGES) {
207        printMessage(msg);
208      }
209          DumpMessage.dumpMsg(msg);
210          
211          System.out.println("\n\n*****************************************\n\n");
212
213      // Now create encrypted messages with different content encryption algorithms
214          
215          msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CBC.clone(), 128);
216      System.out.println("creating encrypted message [AES/128]...");
217      baos.reset();
218      msg.saveChanges();
219      msg.writeTo(baos);
220      bais = new ByteArrayInputStream(baos.toByteArray());
221      msg = new MimeMessage(session, bais);
222      if (PRINT_MESSAGES) {
223        printMessage(msg);
224      }
225      DumpMessage.dumpEncryptedMessage(msg);
226      
227      System.out.println("\n\n*****************************************\n\n");
228      
229      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CBC.clone(), 192);
230      System.out.println("creating encrypted message [AES/192]...");
231      baos.reset();
232      msg.saveChanges();
233      msg.writeTo(baos);
234      bais = new ByteArrayInputStream(baos.toByteArray());
235      msg = new MimeMessage(session, bais);
236      if (PRINT_MESSAGES) {
237        printMessage(msg);
238      }
239      DumpMessage.dumpEncryptedMessage(msg);
240        
241      System.out.println("\n\n*****************************************\n\n");
242        
243      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
244      System.out.println("creating encrypted message [AES/256]...");
245      baos.reset();
246      msg.saveChanges();
247      msg.writeTo(baos);
248      bais = new ByteArrayInputStream(baos.toByteArray());
249      msg = new MimeMessage(session, bais);
250      if (PRINT_MESSAGES) {
251        printMessage(msg);
252      }
253      DumpMessage.dumpEncryptedMessage(msg);
254       
255      System.out.println("\n\n*****************************************\n\n");
256
257      // Now create an implicitly signed and encrypted message with attachment
258      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CBC.clone(), 128, multipart, true, false);
259      System.out.println("creating implicitly signed and encrypted message [AES/128]...");
260      baos.reset();
261      msg.saveChanges();
262      msg.writeTo(baos);
263      bais = new ByteArrayInputStream(baos.toByteArray());
264      msg = new MimeMessage(session, bais);
265      if (PRINT_MESSAGES) {
266        printMessage(msg);
267      }
268      DumpMessage.dumpEncryptedMessage(msg);
269      
270      System.out.println("\n\n*****************************************\n\n");
271      
272      // Now create an implicitly signed and encrypted message with attachment
273      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CBC.clone(), 192, multipart, true, false);
274      System.out.println("creating implicitly signed and encrypted message [AES/192]...");
275      baos.reset();
276      msg.saveChanges();
277      msg.writeTo(baos);
278      bais = new ByteArrayInputStream(baos.toByteArray());
279      msg = new MimeMessage(session, bais);
280      if (PRINT_MESSAGES) {
281        printMessage(msg);
282      }
283      DumpMessage.dumpEncryptedMessage(msg);
284      
285      System.out.println("\n\n*****************************************\n\n");
286
287      // Now create an explicitly signed and encrypted message with attachment
288      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CBC.clone(), 192, multipart, false, false);;
289      System.out.println("creating explicitly signed and encrypted message [AES/192]...");
290      baos.reset();
291      msg.saveChanges();
292      msg.writeTo(baos);
293      bais = new ByteArrayInputStream(baos.toByteArray());
294      msg = new MimeMessage(session, bais);
295      if (PRINT_MESSAGES) {
296        printMessage(msg);
297      }
298      DumpMessage.dumpEncryptedMessage(msg);
299      
300      System.out.println("\n\n*****************************************\n\n");
301
302      // Now create an implicitly signed and encrypted message with attachment
303      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, multipart, true, false);
304      System.out.println("creating implicitly signed and encrypted message [AES/256]...");
305          baos.reset();
306          msg.saveChanges();
307          msg.writeTo(baos);
308          bais = new ByteArrayInputStream(baos.toByteArray());
309          msg = new MimeMessage(session, bais);
310          if (PRINT_MESSAGES) {
311        printMessage(msg);
312      }
313          DumpMessage.dumpEncryptedMessage(msg);
314          
315          System.out.println("\n\n*****************************************\n\n");
316
317      // Now create an explicitly signed and encrypted message with attachment
318      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, multipart, false, false);;
319      System.out.println("creating explicitly signed and encrypted message [AES/256]...");
320          baos.reset();
321          msg.saveChanges();
322          msg.writeTo(baos);
323          bais = new ByteArrayInputStream(baos.toByteArray());
324          msg = new MimeMessage(session, bais);
325          if (PRINT_MESSAGES) {
326        printMessage(msg);
327      }
328          DumpMessage.dumpEncryptedMessage(msg);
329          
330          System.out.println("\n\n*****************************************\n\n");
331          
332          // Now create authenticated encrypted messages with different content encryption algorithms
333          if (DemoUtil.getIaikProviderVersion() >= 5.62) {
334            
335            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_GCM.clone(), 128);
336        System.out.println("creating authenticated encrypted message [AES-GCM/128]...");
337        baos.reset();
338        msg.saveChanges();
339        msg.writeTo(baos);
340        bais = new ByteArrayInputStream(baos.toByteArray());
341        msg = new MimeMessage(session, bais);
342        if (PRINT_MESSAGES) {
343          printMessage(msg);
344        }
345        DumpMessage.dumpEncryptedMessage(msg);
346         
347        System.out.println("\n\n*****************************************\n\n");
348        
349        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_GCM.clone(), 192);
350        System.out.println("creating authenticated encrypted message [AES-GCM/192]...");
351        baos.reset();
352        msg.saveChanges();
353        msg.writeTo(baos);
354        bais = new ByteArrayInputStream(baos.toByteArray());
355        msg = new MimeMessage(session, bais);
356        if (PRINT_MESSAGES) {
357          printMessage(msg);
358        }
359        DumpMessage.dumpEncryptedMessage(msg); 
360         
361        System.out.println("\n\n*****************************************\n\n");
362            
363            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256);
364            System.out.println("creating authenticated encrypted message [AES-GCM/256]...");
365            baos.reset();
366            msg.saveChanges();
367            msg.writeTo(baos);
368            bais = new ByteArrayInputStream(baos.toByteArray());
369            msg = new MimeMessage(session, bais);
370            if (PRINT_MESSAGES) {
371              printMessage(msg);
372            }
373            DumpMessage.dumpEncryptedMessage(msg);
374             
375            System.out.println("\n\n*****************************************\n\n");
376            
377            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CCM.clone(), 128);
378        System.out.println("creating authenticated encrypted message [AES-CCM/128]...");
379        baos.reset();
380        msg.saveChanges();
381        msg.writeTo(baos);
382        bais = new ByteArrayInputStream(baos.toByteArray());
383        msg = new MimeMessage(session, bais);
384        if (PRINT_MESSAGES) {
385          printMessage(msg);
386        }
387        DumpMessage.dumpEncryptedMessage(msg);
388         
389        System.out.println("\n\n*****************************************\n\n");
390        
391        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CCM.clone(), 192);
392        System.out.println("creating authenticated encrypted message [AES-CCM/192]...");
393        baos.reset();
394        msg.saveChanges();
395        msg.writeTo(baos);
396        bais = new ByteArrayInputStream(baos.toByteArray());
397        msg = new MimeMessage(session, bais);
398        if (PRINT_MESSAGES) {
399          printMessage(msg);
400        }
401        DumpMessage.dumpEncryptedMessage(msg); 
402        
403        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), 256);
404        System.out.println("creating authenticated encrypted message [AES-CCM/256]...");
405        baos.reset();
406        msg.saveChanges();
407        msg.writeTo(baos);
408        bais = new ByteArrayInputStream(baos.toByteArray());
409        msg = new MimeMessage(session, bais);
410        if (PRINT_MESSAGES) {
411          printMessage(msg);
412        }
413        DumpMessage.dumpEncryptedMessage(msg);
414         
415        System.out.println("\n\n*****************************************\n\n");
416        
417        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256);
418        System.out.println("creating authenticated encrypted message [ChaChaPoly1305]...");
419        baos.reset();
420        msg.saveChanges();
421        msg.writeTo(baos);
422        bais = new ByteArrayInputStream(baos.toByteArray());
423        msg = new MimeMessage(session, bais);
424        if (PRINT_MESSAGES) {
425          printMessage(msg);
426        }
427        DumpMessage.dumpEncryptedMessage(msg);  
428         
429        System.out.println("\n\n*****************************************\n\n");
430        
431       // Create an implicitly signed and authenticated encrypted message with attachment
432        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_GCM.clone(), 128, multipart, true, true);
433        System.out.println("creating implicitly signed and authenticated encrypted message [AES-GCM/128]...");
434        baos.reset();
435        msg.saveChanges();
436        msg.writeTo(baos);
437        bais = new ByteArrayInputStream(baos.toByteArray());
438        msg = new MimeMessage(session, bais);
439        if (PRINT_MESSAGES) {
440          printMessage(msg);
441        }
442        DumpMessage.dumpEncryptedMessage(msg);
443        
444        System.out.println("\n\n*****************************************\n\n");
445
446        // Create an explicitly signed and authenticated encrypted message with attachment
447        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_GCM.clone(), 128, multipart, true, true);
448        System.out.println("creating explicitly signed and authenticated encrypted message [AES-GCM/128]...");
449        baos.reset();
450        msg.saveChanges();
451        msg.writeTo(baos);
452        bais = new ByteArrayInputStream(baos.toByteArray());
453        msg = new MimeMessage(session, bais);
454        if (PRINT_MESSAGES) {
455          printMessage(msg);
456        }
457        DumpMessage.dumpEncryptedMessage(msg);
458        
459        System.out.println("\n\n*****************************************\n\n");
460        
461        // Create an implicitly signed and authenticated encrypted message with attachment
462        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_GCM.clone(), 192, multipart, true, true);
463        System.out.println("creating implicitly signed and authenticated encrypted message [AES-GCM/192]...");
464        baos.reset();
465        msg.saveChanges();
466        msg.writeTo(baos);
467        bais = new ByteArrayInputStream(baos.toByteArray());
468        msg = new MimeMessage(session, bais);
469        if (PRINT_MESSAGES) {
470          printMessage(msg);
471        }
472        DumpMessage.dumpEncryptedMessage(msg);
473        
474        System.out.println("\n\n*****************************************\n\n");
475
476        // Create an explicitly signed and authenticated encrypted message with attachment
477        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_GCM.clone(), 192, multipart, true, true);
478        System.out.println("creating explicitly signed and authenticated encrypted message [AES-GCM/192]...");
479        baos.reset();
480        msg.saveChanges();
481        msg.writeTo(baos);
482        bais = new ByteArrayInputStream(baos.toByteArray());
483        msg = new MimeMessage(session, bais);
484        if (PRINT_MESSAGES) {
485          printMessage(msg);
486        }
487        DumpMessage.dumpEncryptedMessage(msg);
488        
489        System.out.println("\n\n*****************************************\n\n");
490            
491        // Create an implicitly signed and authenticated encrypted message with attachment
492        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, multipart, true, true);
493        System.out.println("creating implicitly signed and authenticated encrypted message [AES-GCM/256]...");
494        baos.reset();
495        msg.saveChanges();
496        msg.writeTo(baos);
497        bais = new ByteArrayInputStream(baos.toByteArray());
498        msg = new MimeMessage(session, bais);
499        if (PRINT_MESSAGES) {
500          printMessage(msg);
501        }
502        DumpMessage.dumpEncryptedMessage(msg);
503        
504        System.out.println("\n\n*****************************************\n\n");
505
506        // Create an explicitly signed and authenticated encrypted message with attachment
507        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, multipart, true, true);
508        System.out.println("creating explicitly signed and authenticated encrypted message [AES-GCM/256]...");
509        baos.reset();
510        msg.saveChanges();
511        msg.writeTo(baos);
512        bais = new ByteArrayInputStream(baos.toByteArray());
513        msg = new MimeMessage(session, bais);
514        if (PRINT_MESSAGES) {
515          printMessage(msg);
516        }
517        DumpMessage.dumpEncryptedMessage(msg);
518        
519        System.out.println("\n\n*****************************************\n\n");
520        
521        
522        // Create an implicitly signed and authenticated encrypted message with attachment
523         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CCM.clone(), 128, multipart, true, true);
524         System.out.println("creating implicitly signed and authenticated encrypted message [AES-CCM/128]...");
525         baos.reset();
526         msg.saveChanges();
527         msg.writeTo(baos);
528         bais = new ByteArrayInputStream(baos.toByteArray());
529         msg = new MimeMessage(session, bais);
530         if (PRINT_MESSAGES) {
531           printMessage(msg);
532         }
533         DumpMessage.dumpEncryptedMessage(msg);
534         
535         System.out.println("\n\n*****************************************\n\n");
536
537         // Create an explicitly signed and authenticated encrypted message with attachment
538         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CCM.clone(), 128, multipart, true, true);
539         System.out.println("creating explicitly signed and authenticated encrypted message [AES-CCM/128]...");
540         baos.reset();
541         msg.saveChanges();
542         msg.writeTo(baos);
543         bais = new ByteArrayInputStream(baos.toByteArray());
544         msg = new MimeMessage(session, bais);
545         if (PRINT_MESSAGES) {
546           printMessage(msg);
547         }
548         DumpMessage.dumpEncryptedMessage(msg);
549         
550         System.out.println("\n\n*****************************************\n\n");
551         
552         // Create an implicitly signed and authenticated encrypted message with attachment
553         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CCM.clone(), 192, multipart, true, true);
554         System.out.println("creating implicitly signed and authenticated encrypted message [AES-CCM/192]...");
555         baos.reset();
556         msg.saveChanges();
557         msg.writeTo(baos);
558         bais = new ByteArrayInputStream(baos.toByteArray());
559         msg = new MimeMessage(session, bais);
560         if (PRINT_MESSAGES) {
561           printMessage(msg);
562         }
563         DumpMessage.dumpEncryptedMessage(msg);
564         
565         System.out.println("\n\n*****************************************\n\n");
566
567         // Create an explicitly signed and authenticated encrypted message with attachment
568         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CCM.clone(), 192, multipart, true, true);
569         System.out.println("creating explicitly signed and authenticated encrypted message [AES-CCM/192]...");
570         baos.reset();
571         msg.saveChanges();
572         msg.writeTo(baos);
573         bais = new ByteArrayInputStream(baos.toByteArray());
574         msg = new MimeMessage(session, bais);
575         if (PRINT_MESSAGES) {
576           printMessage(msg);
577         }
578         DumpMessage.dumpEncryptedMessage(msg);
579         
580         System.out.println("\n\n*****************************************\n\n");
581         
582         // Create an implicitly signed and authenticated encrypted message with attachment
583         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), 256, multipart, true, true);
584         System.out.println("creating implicitly signed and authenticated encrypted message [AES-CCM/256]...");
585         baos.reset();
586         msg.saveChanges();
587         msg.writeTo(baos);
588         bais = new ByteArrayInputStream(baos.toByteArray());
589         msg = new MimeMessage(session, bais);
590         if (PRINT_MESSAGES) {
591           printMessage(msg);
592         }
593         DumpMessage.dumpEncryptedMessage(msg);
594         
595         System.out.println("\n\n*****************************************\n\n");
596
597         // Create an explicitly signed and authenticated encrypted message with attachment
598         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), 256, multipart, true, true);
599         System.out.println("creating explicitly signed and authenticated encrypted message [AES-CCM/256]...");
600         baos.reset();
601         msg.saveChanges();
602         msg.writeTo(baos);
603         bais = new ByteArrayInputStream(baos.toByteArray());
604         msg = new MimeMessage(session, bais);
605         if (PRINT_MESSAGES) {
606           printMessage(msg);
607         }
608         DumpMessage.dumpEncryptedMessage(msg);
609         
610         System.out.println("\n\n*****************************************\n\n");
611        
612         // Create an implicitly signed and authenticated encrypted message with attachment
613         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, multipart, true, true);
614         System.out.println("creating implicitly signed and authenticated encrypted message [ChaCha20Poly1305]...");
615         baos.reset();
616         msg.saveChanges();
617         msg.writeTo(baos);
618         bais = new ByteArrayInputStream(baos.toByteArray());
619         msg = new MimeMessage(session, bais);
620         if (PRINT_MESSAGES) {
621           printMessage(msg);
622         }
623         DumpMessage.dumpEncryptedMessage(msg);
624         
625         System.out.println("\n\n*****************************************\n\n");
626
627         // Create an explicitly signed and authenticated encrypted message with attachment
628         msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, multipart, true, true);
629         System.out.println("creating explicitly signed and authenticated encrypted message [ChaCha20Poly1305]...");
630         baos.reset();
631         msg.saveChanges();
632         msg.writeTo(baos);
633         bais = new ByteArrayInputStream(baos.toByteArray());
634         msg = new MimeMessage(session, bais);
635         if (PRINT_MESSAGES) {
636           printMessage(msg);
637         }
638         DumpMessage.dumpEncryptedMessage(msg);
639         
640         System.out.println("\n\n*****************************************\n\n");
641            
642          }
643         
644          // certs only message
645          msg = createCertsOnlyMessage(session);
646          System.out.println("creating certs-only message");
647          baos.reset();
648          msg.saveChanges();
649          msg.writeTo(baos);
650          bais = new ByteArrayInputStream(baos.toByteArray());
651          msg = new MimeMessage(session, bais);
652          if (PRINT_MESSAGES) {
653        printMessage(msg);
654      }
655          DumpMessage.dumpMsg(msg);
656          
657          System.out.println("\n\n*****************************************\n\n");
658
659          // second certs only message
660          msg = createCertsOnlyMultiPartMessage(session);
661          System.out.println("creating message with certs-only part");
662          baos.reset();
663          msg.saveChanges();
664          msg.writeTo(baos);
665          bais = new ByteArrayInputStream(baos.toByteArray());
666          msg = new MimeMessage(session, bais);
667          if (PRINT_MESSAGES) {
668        printMessage(msg);
669      }
670          DumpMessage.dumpMsg(msg);
671          
672          System.out.println("\n\n*****************************************\n\n");
673
674          // application/pkcs10 cert request message
675      msg = createPKCS10Message(session);
676      System.out.println("creating application/pkcs10 message...");
677          baos.reset();
678          msg.saveChanges();
679          msg.writeTo(baos);
680          bais = new ByteArrayInputStream(baos.toByteArray());
681          msg = new MimeMessage(session, bais);
682          if (PRINT_MESSAGES) {
683        printMessage(msg);
684      }
685          DumpMessage.dumpMsg(msg);
686          
687          System.out.println("\n\n*****************************************\n\n");
688
689          // sending application/pkcs10 message where the request is in the second part
690          msg = createPKCS10MultiPartMessage(session);
691          System.out.println("creating message with pkcs10 part...");
692          baos.reset();
693          msg.saveChanges();
694          msg.writeTo(baos);
695          bais = new ByteArrayInputStream(baos.toByteArray());
696          msg = new MimeMessage(session, bais);
697          if (PRINT_MESSAGES) {
698        printMessage(msg);
699      }
700          DumpMessage.dumpMsg(msg);
701          
702          System.out.println("\n\n*****************************************\n\n");
703
704          // compressed message 
705          msg = createCompressedMessage(session, multipart, (AlgorithmID)CMSAlgorithmID.zlib_compress.clone());
706          System.out.println("creating message with compressed data...");
707          baos.reset();
708          msg.saveChanges();
709          msg.writeTo(baos);
710          bais = new ByteArrayInputStream(baos.toByteArray());
711          msg = new MimeMessage(session, bais);
712          if (PRINT_MESSAGES) {
713        printMessage(msg);
714      }
715          DumpMessage.dumpMsg(msg);
716
717        } catch (Exception ex) {
718            ex.printStackTrace();
719            throw new RuntimeException(ex.toString());
720        }
721  }
722
723  /**
724   * Creates a MIME message container with the given subject for the given session.
725   * 
726   * @param session the mail sesion
727   * @param subject the subject of the message
728   *
729   * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
730   *
731   * @throws MessagingException if the message cannot be created
732   */
733  public Message createMessage(Session session, String subject) throws MessagingException {
734    MimeMessage msg = new MimeMessage(session);
735    msg.setFrom(new InternetAddress(from_));
736        msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to_, false));
737        msg.setSentDate(new Date());
738    msg.setSubject(subject);
739    return msg;
740  }
741  
742  /**
743   * Creates a simple plain (neither signed nor encrypted) message.
744   *
745   * @param session the mail session
746   * @param dataHandler the content of the message
747   * 
748   * @return the plain message
749   *
750   * @throws MessagingException if an error occurs when creating the message
751   */
752  public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException {
753
754    Message msg = createMessage(session, "IAIK-S/MIME: Plain message");
755    if (dataHandler != null) {
756      msg.setDataHandler(dataHandler);
757    } else {
758      msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n");
759    }
760    return msg;
761  }
762  
763  /**
764   * Creates a signed and encrypted message.
765   *
766   * @param session the mail session
767   * @param contentEA the content encryption algorithm to be used
768   * @param keyLength the length of the secret content encryption key to be created and used
769   * @param dataHandler the content of the message to be signed and encrypted
770   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
771   *                 (multipart/signed) signing
772   * @param authEncrypt whether to create an authenticated encrypted message                
773   * 
774   * @return the signed and encrypted message
775   *
776   * @throws MessagingException if an error occurs when creating the message
777   */
778  public Message createSignedAndEncryptedMessage(Session session, 
779    AlgorithmID contentEA, int keyLength,
780    DataHandler dataHandler, boolean implicit, boolean authEncrypt)
781    throws MessagingException {
782
783    String subject = null;
784    String text = null;
785    if (implicit) {
786      subject = "IAIK-S/MIME: Implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted";
787      text = "This message is implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted!\n\n\n";
788    } else {
789      subject = "IAIK-S/MIME: explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted";
790      text = "This message is explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted!\n\n\n";
791    }
792    Message msg = createMessage(session, subject);
793
794    SignedContent sc = new SignedContent(implicit);
795    if (dataHandler != null) {
796      sc.setDataHandler(dataHandler);
797    } else {
798      sc.setText(text);
799    }
800    sc.setCertificates(signerCertificates_);
801    try {
802      sc.addSigner(signerPrivateKey_, 
803                   signerCertificate_,
804                   (AlgorithmID)AlgorithmID.sha256.clone(),
805                   (AlgorithmID)AlgorithmID.rsaEncryption.clone());
806    } catch (NoSuchAlgorithmException ex) {
807      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
808    }
809
810    EncryptedContent ec = null;
811    if (authEncrypt) {
812      ec = new AuthEncryptedContent(sc);
813    } else {
814      ec = new EncryptedContent(sc);
815    }
816
817    
818    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
819    
820    // sender wants to be able to decrypt the message
821    ec.addRecipient(senderCryptCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());  
822     
823    // add RSA recipient
824    ec.addRecipient(rsaRecipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
825    // set the encryption algorithm
826    try {
827      ec.setEncryptionAlgorithm(algorithm, keyLength);
828    } catch (NoSuchAlgorithmException ex) {
829      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
830    }   
831    msg.setContent(ec, ec.getContentType());
832    // let the EncryptedContent update some message headers
833    ec.setHeaders(msg);
834
835    return msg;
836  }
837  
838  /**
839   * Creates a signed message.
840   *
841   * @param session the mail session
842   * @param dataHandler the content of the message to be signed
843   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
844   *                 (multipart/signed) signing
845   * @param digestAlgorithm the digest algorithm to be used
846   * @param signatureAlgorithm the signature algorithm to be used                
847   * 
848   * @return the signed message
849   *
850   * @throws MessagingException if an error occurs when creating the message
851   */
852  public Message createSignedMessage(Session session, 
853                                     DataHandler dataHandler,
854                                     boolean implicit,
855                                     AlgorithmID digestAlgorithm,
856                                     AlgorithmID signatureAlgorithm)
857      throws MessagingException {
858
859    String subject = null;
860    StringBuffer buf = new StringBuffer();
861    
862    if (implicit) {
863      subject = "IAIK-S/MIME: Implicitly Signed";
864      buf.append("This message is implicitly signed!\n");
865      buf.append("You need an S/MIME aware mail client to view this message.\n");
866      buf.append("\n\n");
867    } else {
868      subject = "IAIK-S/MIME: Explicitly Signed";
869      buf.append("This message is explicitly signed!\n");
870      buf.append("Every mail client can view this message.\n");
871      buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
872      buf.append("\n\n");
873    }
874    
875    Message msg = createMessage(session, subject);
876    
877    SignedContent sc = new SignedContent(implicit);
878    if (dataHandler != null) {
879      sc.setDataHandler(dataHandler);
880    } else {
881      sc.setText(buf.toString());
882    }
883    sc.setCertificates(signerCertificates_);
884
885    try {
886      sc.addSigner(signerPrivateKey_, 
887                   signerCertificate_,
888                   (AlgorithmID)digestAlgorithm.clone(),
889                   (AlgorithmID)signatureAlgorithm.clone());
890    } catch (NoSuchAlgorithmException ex) {
891      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
892    }
893
894    msg.setContent(sc, sc.getContentType());
895    // let the SignedContent update some message headers
896    sc.setHeaders(msg);
897    return msg;
898  }
899  
900  /**
901   * Creates an encrypted message.
902   *
903   * @param session the mail session
904   * @param contentEA the content encryption algorithm to be used
905   * @param keyLength the length of the secret content encryption key to be created and used
906   * 
907   * @return the encrypted message
908   *
909   * @throws MessagingException if an error occurs when creating the message
910   */
911  public Message createEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength)
912    throws MessagingException {
913    
914    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
915    
916    StringBuffer subject = new StringBuffer();
917    subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName());
918    if (keyLength > 0) {
919      subject.append("/"+keyLength);
920    }  
921    subject.append("]");
922    Message msg = createMessage(session, subject.toString());
923
924    EncryptedContent ec = new EncryptedContent();
925
926    StringBuffer buf = new StringBuffer();
927    buf.append("This is the encrypted content!\n");
928    buf.append("Content encryption algorithm: "+algorithm.getName());
929    buf.append("\n\n");
930
931    ec.setText(buf.toString());
932    
933    // sender wants to be able to decrypt the message
934    ec.addRecipient(senderCryptCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());  
935   
936    // add RSA recipient
937    ec.addRecipient(rsaRecipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
938    try {
939      ec.setEncryptionAlgorithm(algorithm, keyLength);
940    } catch (NoSuchAlgorithmException ex) {
941      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
942    }    
943
944    msg.setContent(ec, ec.getContentType());
945    // let the EncryptedContent update some message headers
946    ec.setHeaders(msg);
947
948    return msg;
949  }
950  
951  /**
952   * Creates an authenticated encrypted message.
953   *
954   * @param session the mail session
955   * @param contentEA the content encryption algorithm to be used
956   * @param keyLength the length of the secret content encryption key to be created and used
957   * 
958   * @return the encrypted message
959   *
960   * @throws MessagingException if an error occurs when creating the message
961   */
962  public Message createAuthEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength)
963      throws MessagingException {
964    
965    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
966   
967    StringBuffer subject = new StringBuffer();
968    subject.append("IAIK-S/MIME: Authenticated Encrypted ["+algorithm.getName());
969    if (keyLength > 0) {
970      subject.append("/"+keyLength);
971    }  
972    subject.append("]");
973    Message msg = createMessage(session, subject.toString());
974
975    AuthEncryptedContent ec = new AuthEncryptedContent();
976
977    StringBuffer buf = new StringBuffer();
978    buf.append("This is the authenticated encrypted content!\n");
979    buf.append("Content encryption algorithm: "+algorithm.getName());
980    buf.append("\n\n");
981
982    ec.setText(buf.toString());
983    
984    // sender wants to be able to decrypt the message
985    ec.addRecipient(senderCryptCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 
986    
987    // add RSA recipient 
988    ec.addRecipient(rsaRecipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
989    try {
990      ec.setEncryptionAlgorithm(algorithm, keyLength);
991    } catch (NoSuchAlgorithmException ex) {
992      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
993    }    
994
995    msg.setContent(ec, ec.getContentType());
996    // let the EncryptedContent update some message headers
997    ec.setHeaders(msg);
998
999    return msg;
1000  }
1001  
1002  /**
1003   * Creates a certs-only message.
1004   *
1005   * @param session the mail session
1006   * 
1007   * @return the certs-only message
1008   *
1009   * @throws MessagingException if an error occurs when creating the message
1010   */
1011  public Message createCertsOnlyMessage(Session session)
1012      throws MessagingException {
1013
1014    Message msg = createMessage(session, "IAIK S/MIME: Certs-only message");
1015    //use new content types
1016    SMimeParameters.useNewContentTypes(true);
1017    SignedContent sc = new SignedContent(true, SignedContent.CERTS_ONLY);
1018    sc.setCertificates(signerCertificates_);
1019    msg.setContent(sc, sc.getContentType());
1020    //set filename and attachment parameters
1021    sc.setHeaders(msg);
1022
1023    return msg;
1024  }
1025  
1026  
1027  /**
1028   * Creates a certs-only message where the certificate list is transferred as attachment.
1029   *
1030   * @param session the mail session
1031   * 
1032   * @return the certs-only message
1033   *
1034   * @throws MessagingException if an error occurs when creating the message
1035   */
1036  public Message createCertsOnlyMultiPartMessage(Session session) throws MessagingException {
1037
1038    MimeBodyPart mbp1 = new MimeBodyPart();
1039        mbp1.setText("This is a test where the certs-only message is included in the second part!\n\n");
1040
1041    MimeBodyPart attachment = new MimeBodyPart();
1042    //use new content types
1043    SMimeParameters.useNewContentTypes(true);
1044    SignedContent sc = new SignedContent(true, SignedContent.CERTS_ONLY);
1045    sc.setCertificates(signerCertificates_);
1046    attachment.setContent(sc, sc.getContentType());
1047    // let the SignedContent update some headers
1048    sc.setHeaders(attachment);
1049    Multipart mp = new MimeMultipart();
1050    mp.addBodyPart(mbp1);
1051    mp.addBodyPart(attachment);
1052
1053    Message msg = createMessage(session, "IAIK S/MIME: Certs-only multipart message");
1054    msg.setContent(mp, mp.getContentType());
1055    return msg;
1056  }
1057  
1058   /**
1059   * Creates a compressed message.
1060   *
1061   * @param session the mail session
1062   * @param dataHandler the datahandler supplying the content to be compressed
1063   * @param algorithm the compression algorithm to be used
1064   * 
1065   * @return the compressed message
1066   *
1067   * @throws MessagingException if an error occurs when creating the message
1068   */
1069  public Message createCompressedMessage(Session session, DataHandler dataHandler, AlgorithmID algorithm)
1070      throws MessagingException {
1071
1072    String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]";
1073    Message msg = createMessage(session, subject.toString());
1074
1075    CompressedContent compressedContent = new CompressedContent();
1076    
1077    if (dataHandler == null) {
1078      StringBuffer buf = new StringBuffer();
1079      buf.append("This is the compressed content!\n");
1080      buf.append("Compression algorithm: "+algorithm.getName());
1081      buf.append("\n\n");
1082      compressedContent.setText(buf.toString());
1083    } else {
1084      compressedContent.setDataHandler(dataHandler);   
1085    }    
1086    
1087    try {
1088      compressedContent.setCompressionAlgorithm(algorithm);
1089    } catch (NoSuchAlgorithmException ex) {
1090      throw new MessagingException("Compression algorithm not supported: " + ex.getMessage());   
1091    }   
1092
1093    msg.setContent(compressedContent, compressedContent.getContentType());
1094    // let the CompressedContent update some message headers
1095    compressedContent.setHeaders(msg);
1096
1097    return msg;
1098  }
1099
1100  /**
1101   * Creates a PKCS#10 certificate request message.
1102   *
1103   * @param session the mail session
1104   * 
1105   * @return the PKCS#10 certificate request message
1106   *
1107   * @throws MessagingException if an error occurs when creating the message
1108   */
1109  public Message createPKCS10Message(Session session)
1110    throws MessagingException {
1111
1112    Message msg = createMessage(session, "IAIK-S/MIME: Certificate Request");
1113
1114    PKCS10Content pc = new PKCS10Content();
1115    CertificateRequest request = null;
1116    try {
1117       request = createCertificateRequest();
1118    } catch (PKCSException ex) {
1119       throw new MessagingException(ex.getMessage());
1120    }
1121    pc.setCertRequest(request);
1122    msg.setContent(pc, pc.getContentType());
1123    // let the PKCS10Content update some message headers
1124    pc.setHeaders(msg);
1125
1126    return msg;
1127  }
1128
1129
1130  private CertificateRequest createCertificateRequest() throws PKCSException {
1131    try {
1132      Name subject = new Name();
1133          subject.addRDN(ObjectID.commonName, firstName_ + " " + lastName_);
1134          subject.addRDN(ObjectID.emailAddress, from_);
1135          CertificateRequest certRequest;
1136      certRequest = new CertificateRequest(signerCertificate_.getPublicKey(), subject);
1137          certRequest.sign((AlgorithmID)AlgorithmID.sha256WithRSAEncryption.clone(), signerPrivateKey_);
1138          certRequest.verify();
1139          return certRequest;
1140        } catch (Exception ex) {
1141          throw new PKCSException("Cannot create cert request: " + ex.getMessage());
1142        }
1143
1144  }
1145  
1146  /**
1147   * Creates a PKCS#10 message where the certificate request is transferred as attachment.
1148   *
1149   * @param session the mail session
1150   * 
1151   * @return the PKCS#10 certificate request message
1152   *
1153   * @throws MessagingException if an error occurs when creating the message
1154   */
1155  public Message createPKCS10MultiPartMessage(Session session) throws MessagingException {
1156
1157    MimeBodyPart mbp1 = new MimeBodyPart();
1158        mbp1.setText("This is a test where the request message is included in the second part!\n\n");
1159        // try to test an attachment
1160        // this demo attaches our homepage
1161    MimeBodyPart attachment = new MimeBodyPart();
1162    //use new content types
1163    SMimeParameters.useNewContentTypes(true);
1164    PKCS10Content pc = new PKCS10Content();
1165    CertificateRequest request = null;
1166    try {
1167      request = createCertificateRequest();
1168    } catch (PKCSException ex) {
1169      throw new MessagingException(ex.getMessage());
1170    }
1171    pc.setCertRequest(request);
1172    DataHandler pkcs10Handler = new DataHandler(pc, pc.getContentType());
1173    attachment.setDataHandler(pkcs10Handler);
1174    attachment.setDisposition("attachment");
1175    attachment.setFileName("smime.p10");
1176    Multipart mp = new MimeMultipart();
1177    mp.addBodyPart(mbp1);
1178    mp.addBodyPart(attachment);
1179
1180    Message msg = createMessage(session, "IAIK-S/MIME: Certificate Request multipart message");
1181    msg.setContent(mp, mp.getContentType());
1182    return msg;
1183  }
1184  
1185  /** 
1186   * Prints a dump of the given message to System.out.
1187   *
1188   * @param msg the message to be dumped to System.out
1189   *
1190   * @throws IOException if an I/O error occurs
1191   */
1192  static void printMessage(Message msg) throws IOException {
1193    System.out.println("------------------------------------------------------------------");
1194    System.out.println("Message dump: \n");
1195    try {
1196      msg.writeTo(System.out);
1197    } catch (MessagingException ex) {
1198      throw new IOException(ex.getMessage());   
1199    }    
1200    System.out.println("\n------------------------------------------------------------------");
1201  }  
1202  
1203  /**
1204   * The main method.
1205   */
1206  public static void main(String[] argv) throws IOException {
1207     
1208    DemoSMimeUtil.initDemos();
1209        (new SMimeV4Demo()).start();
1210    System.out.println("\nReady!");
1211    DemoUtil.waitKey();
1212  }
1213}