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/SMimeV3Demo.java 49    12.02.25 17:58 Dbratko $
029// $Revision: 49 $
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.SMimeException;
068import iaik.smime.SMimeMultipart;
069import iaik.smime.SMimeParameters;
070import iaik.smime.SignedContent;
071import iaik.x509.X509Certificate;
072
073/**
074 * This class demonstrates the usage of the IAIK S/MIME implementation. It shows how to create
075 * signed and/or encrypted S/MIMEv3 messages and how to parse them and verify the signatures 
076 * and decrypt the content, respectively.
077 * <p>
078 * To run this demo the following packages are required:
079 * <ul>
080 *    <li>
081 *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
082 *    </li>
083 *    <li>
084 *       <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>).
085 *    </li>
086 *    <li>
087 *       <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
088 *    </li>   
089 *    <li>
090 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
091 *    </li> 
092 * </ul>
093 * You also will need the ESDH implementation of IAIK-JCE contained in <code>iaik_esdh.jar</code> or
094 * <code>iaik_jce_full.jar</code>; the first may be used in addition to <code>iaik_jce.jar</code>,
095 * the second instead of <code>iaik_jce.jar</code>.
096 */
097public class SMimeV3Demo {
098    
099  // whether to print dump all generates test messages to System.out
100  final static boolean PRINT_MESSAGES = false;   
101  
102
103  String firstName = "John";
104  String lastName = "SMime";
105  String to = "smimetest@iaik.tugraz.at";     // email recipient
106  String from = "smimetest@iaik.tugraz.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 SMimeV3Demo() {
119    
120    System.out.println();
121    System.out.println("********************************************************************************************");
122    System.out.println("*                                SMimeV3Demo demo                                          *");
123    System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIMEv3 messages) *");
124    System.out.println("********************************************************************************************");
125    System.out.println();
126    
127    // get the certificates from the KeyStore
128    signerCertificates = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
129    signerPrivateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
130    signerCertificate = signerCertificates[0];
131
132    // recipient = signer for this test
133    recipientCertificate = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1)[0];
134    if (recipientCertificate == null) {
135      throw new NullPointerException("ESDH certificate not available! Add iaik_esdh.jar to your classpath!");
136    }
137    encryptionCertOfSigner = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
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      // 1. 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      // 2. This is an explicitly signed message
183      msg = createSignedMessage(session, multipart, false, AlgorithmID.sha256, AlgorithmID.dsaWithSHA256);
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      // 3. This is an implicitly signed message
199      msg = createSignedMessage(session, multipart, true, AlgorithmID.sha256, AlgorithmID.dsaWithSHA256);
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      // 4. Now create encrypted messages with different content encryption algorithms
214          
215          // RC2 is deprecated; only demonstrated here
216      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 40, 
217        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)AlgorithmID.cms_rc2_wrap.clone(), 128);
218      System.out.println("creating encrypted message [RC2/40]...");
219          baos.reset();
220          msg.saveChanges();
221          msg.writeTo(baos);
222          bais = new ByteArrayInputStream(baos.toByteArray());
223          msg = new MimeMessage(session, bais);
224          if (PRINT_MESSAGES) {
225        printMessage(msg);
226      }
227          DumpMessage.dumpMsg(msg);
228          
229          System.out.println("\n\n*****************************************\n\n");
230          
231          // RC2 is deprecated; only demonstrated here
232      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 64, 
233        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)AlgorithmID.cms_rc2_wrap.clone(), 128);
234      System.out.println("creating encrypted message [RC2/64]...");
235          baos.reset();
236          msg.saveChanges();
237          msg.writeTo(baos);
238          bais = new ByteArrayInputStream(baos.toByteArray());
239          msg = new MimeMessage(session, bais);
240          if (PRINT_MESSAGES) {
241        printMessage(msg);
242      }
243          DumpMessage.dumpMsg(msg);
244          
245          System.out.println("\n\n*****************************************\n\n");
246          
247          // RC2 is deprecated; only demonstrated here
248      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128,
249        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)AlgorithmID.cms_rc2_wrap.clone(), 128);
250      System.out.println("creating encrypted message [RC2/128]...");
251          baos.reset();
252          msg.saveChanges();
253          msg.writeTo(baos);
254          bais = new ByteArrayInputStream(baos.toByteArray());
255          msg = new MimeMessage(session, bais);
256          if (PRINT_MESSAGES) {
257        printMessage(msg);
258      }
259          DumpMessage.dumpMsg(msg);
260          
261          
262          System.out.println("\n\n*****************************************\n\n");
263          
264          // DES EDE is deprecated; only demonstrated here
265      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.des_EDE3_CBC.clone(), 192,
266        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)AlgorithmID.cms_3DES_wrap.clone(), 192);
267      System.out.println("creating encrypted message [TripleDES]...");
268          baos.reset();
269          msg.saveChanges();
270          msg.writeTo(baos);
271          bais = new ByteArrayInputStream(baos.toByteArray());
272          msg = new MimeMessage(session, bais);
273          if (PRINT_MESSAGES) {
274        printMessage(msg);
275      }
276          DumpMessage.dumpMsg(msg);
277      
278      System.out.println("\n\n*****************************************\n\n");
279      
280      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CBC.clone(), 128,
281        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes128_wrap.clone(), 128);
282      System.out.println("creating encrypted message [AES/128]...");
283      baos.reset();
284      msg.saveChanges();
285      msg.writeTo(baos);
286      bais = new ByteArrayInputStream(baos.toByteArray());
287      msg = new MimeMessage(session, bais);
288      if (PRINT_MESSAGES) {
289        printMessage(msg);
290      }
291      DumpMessage.dumpMsg(msg);
292      
293      System.out.println("\n\n*****************************************\n\n");
294      
295      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CBC.clone(), 192,
296        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes192_wrap.clone(), 192);
297      System.out.println("creating encrypted message [AES/192]...");
298      baos.reset();
299      msg.saveChanges();
300      msg.writeTo(baos);
301      bais = new ByteArrayInputStream(baos.toByteArray());
302      msg = new MimeMessage(session, bais);
303      if (PRINT_MESSAGES) {
304        printMessage(msg);
305      }
306      DumpMessage.dumpMsg(msg);
307        
308      System.out.println("\n\n*****************************************\n\n");
309        
310      msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256,
311        (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone(), 256);
312      System.out.println("creating encrypted message [AES/256]...");
313      baos.reset();
314      msg.saveChanges();
315      msg.writeTo(baos);
316      bais = new ByteArrayInputStream(baos.toByteArray());
317      msg = new MimeMessage(session, bais);
318      if (PRINT_MESSAGES) {
319        printMessage(msg);
320      }
321      DumpMessage.dumpMsg(msg);  
322       
323      
324      System.out.println("\n\n*****************************************\n\n");
325
326      // 5. Now create an implicitly signed and encrypted message with attachment
327      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), multipart, true);
328      System.out.println("creating implicitly signed and encrypted message [AES/256]...");
329          baos.reset();
330          msg.saveChanges();
331          msg.writeTo(baos);
332          bais = new ByteArrayInputStream(baos.toByteArray());
333          msg = new MimeMessage(session, bais);
334          if (PRINT_MESSAGES) {
335        printMessage(msg);
336      }
337          DumpMessage.dumpMsg(msg);
338          
339          System.out.println("\n\n*****************************************\n\n");
340
341      // 6. Now create an explicitly signed and encrypted message with attachment
342      msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), multipart, false);
343      System.out.println("creating explicitly signed and encrypted message [AES/256]...");
344          baos.reset();
345          msg.saveChanges();
346          msg.writeTo(baos);
347          bais = new ByteArrayInputStream(baos.toByteArray());
348          msg = new MimeMessage(session, bais);
349          if (PRINT_MESSAGES) {
350        printMessage(msg);
351      }
352          DumpMessage.dumpMsg(msg);
353          
354          System.out.println("\n\n*****************************************\n\n");
355          
356          // 7. Now create authenticated encrypted messages with different content encryption algorithms
357          if (DemoUtil.getIaikProviderVersion() >= 5.62) {
358            
359            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_GCM.clone(), 128,
360            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes128_wrap.clone(), 128);
361        System.out.println("creating authenticated encrypted message [AES-GCM/128]...");
362        baos.reset();
363        msg.saveChanges();
364        msg.writeTo(baos);
365        bais = new ByteArrayInputStream(baos.toByteArray());
366        msg = new MimeMessage(session, bais);
367        if (PRINT_MESSAGES) {
368          printMessage(msg);
369        }
370        DumpMessage.dumpMsg(msg);  
371         
372        System.out.println("\n\n*****************************************\n\n");
373        
374        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_GCM.clone(), 192,
375            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes192_wrap.clone(), 192);
376        System.out.println("creating authenticated encrypted message [AES-GCM/192]...");
377        baos.reset();
378        msg.saveChanges();
379        msg.writeTo(baos);
380        bais = new ByteArrayInputStream(baos.toByteArray());
381        msg = new MimeMessage(session, bais);
382        if (PRINT_MESSAGES) {
383          printMessage(msg);
384        }
385        DumpMessage.dumpMsg(msg);  
386         
387        System.out.println("\n\n*****************************************\n\n");
388            
389            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256,
390                (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone(), 256);
391            System.out.println("creating authenticated encrypted message [AES-GCM/256]...");
392            baos.reset();
393            msg.saveChanges();
394            msg.writeTo(baos);
395            bais = new ByteArrayInputStream(baos.toByteArray());
396            msg = new MimeMessage(session, bais);
397            if (PRINT_MESSAGES) {
398              printMessage(msg);
399            }
400            DumpMessage.dumpMsg(msg);  
401             
402            System.out.println("\n\n*****************************************\n\n");
403            
404            msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes128_CCM.clone(), 128,
405            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes128_wrap.clone(), 128);
406        System.out.println("creating authenticated encrypted message [AES-CCM/128]...");
407        baos.reset();
408        msg.saveChanges();
409        msg.writeTo(baos);
410        bais = new ByteArrayInputStream(baos.toByteArray());
411        msg = new MimeMessage(session, bais);
412        if (PRINT_MESSAGES) {
413          printMessage(msg);
414        }
415        DumpMessage.dumpMsg(msg);  
416         
417        System.out.println("\n\n*****************************************\n\n");
418        
419        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes192_CCM.clone(), 192,
420            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes192_wrap.clone(), 192);
421        System.out.println("creating authenticated encrypted message [AES-CCM/192]...");
422        baos.reset();
423        msg.saveChanges();
424        msg.writeTo(baos);
425        bais = new ByteArrayInputStream(baos.toByteArray());
426        msg = new MimeMessage(session, bais);
427        if (PRINT_MESSAGES) {
428          printMessage(msg);
429        }
430        DumpMessage.dumpMsg(msg);  
431        
432        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), 256,
433            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone(), 256);
434        System.out.println("creating authenticated encrypted message [AES-CCM/256]...");
435        baos.reset();
436        msg.saveChanges();
437        msg.writeTo(baos);
438        bais = new ByteArrayInputStream(baos.toByteArray());
439        msg = new MimeMessage(session, bais);
440        if (PRINT_MESSAGES) {
441          printMessage(msg);
442        }
443        DumpMessage.dumpMsg(msg);  
444         
445        System.out.println("\n\n*****************************************\n\n");
446        
447        msg = createAuthEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256,
448            (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone(), 256);
449        System.out.println("creating authenticated encrypted message [ChaChaPoly1305]...");
450        baos.reset();
451        msg.saveChanges();
452        msg.writeTo(baos);
453        bais = new ByteArrayInputStream(baos.toByteArray());
454        msg = new MimeMessage(session, bais);
455        if (PRINT_MESSAGES) {
456          printMessage(msg);
457        }
458        DumpMessage.dumpMsg(msg);  
459         
460        System.out.println("\n\n*****************************************\n\n");
461            
462        // Create an implicitly signed and authenticated encrypted message with attachment
463        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), multipart, true);
464        System.out.println("creating implicitly signed and encrypted message [AES-GCM/256]...");
465        baos.reset();
466        msg.saveChanges();
467        msg.writeTo(baos);
468        bais = new ByteArrayInputStream(baos.toByteArray());
469        msg = new MimeMessage(session, bais);
470        if (PRINT_MESSAGES) {
471          printMessage(msg);
472        }
473        DumpMessage.dumpMsg(msg);
474        
475        System.out.println("\n\n*****************************************\n\n");
476
477        // Create an explicitly signed and encrypted message with attachment
478        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), multipart, false);
479        System.out.println("creating explicitly signed and encrypted message [AES-GCM/256]...");
480        baos.reset();
481        msg.saveChanges();
482        msg.writeTo(baos);
483        bais = new ByteArrayInputStream(baos.toByteArray());
484        msg = new MimeMessage(session, bais);
485        if (PRINT_MESSAGES) {
486          printMessage(msg);
487        }
488        DumpMessage.dumpMsg(msg);
489        
490        System.out.println("\n\n*****************************************\n\n");
491        
492        // Create an implicitly signed and authenticated encrypted message with attachment
493        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), multipart, true);
494        System.out.println("creating implicitly signed and encrypted message [AES-CCM/256]...");
495        baos.reset();
496        msg.saveChanges();
497        msg.writeTo(baos);
498        bais = new ByteArrayInputStream(baos.toByteArray());
499        msg = new MimeMessage(session, bais);
500        if (PRINT_MESSAGES) {
501          printMessage(msg);
502        }
503        DumpMessage.dumpMsg(msg);
504        
505        System.out.println("\n\n*****************************************\n\n");
506
507        // Create an explicitly signed and encrypted message with attachment
508        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CCM.clone(), multipart, false);
509        System.out.println("creating explicitly signed and encrypted message [AES-CCM/256]...");
510        baos.reset();
511        msg.saveChanges();
512        msg.writeTo(baos);
513        bais = new ByteArrayInputStream(baos.toByteArray());
514        msg = new MimeMessage(session, bais);
515        if (PRINT_MESSAGES) {
516          printMessage(msg);
517        }
518        DumpMessage.dumpMsg(msg);
519        
520        System.out.println("\n\n*****************************************\n\n");
521        
522        // Create an implicitly signed and authenticated encrypted message with attachment
523        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), multipart, true);
524        System.out.println("creating implicitly signed and encrypted message [ChaCha20Poly1305]...");
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.dumpMsg(msg);
534        
535        System.out.println("\n\n*****************************************\n\n");
536
537        // Create an explicitly signed and encrypted message with attachment
538        msg = createSignedAndEncryptedMessage(session, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), multipart, false);
539        System.out.println("creating explicitly signed and encrypted message [ChaCha20Poly1305]...");
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.dumpMsg(msg);
549        
550        System.out.println("\n\n*****************************************\n\n");
551            
552          }
553         
554          // 8. certs only message
555          msg = createCertsOnlyMessage(session);
556          System.out.println("creating certs-only message");
557          baos.reset();
558          msg.saveChanges();
559          msg.writeTo(baos);
560          bais = new ByteArrayInputStream(baos.toByteArray());
561          msg = new MimeMessage(session, bais);
562          if (PRINT_MESSAGES) {
563        printMessage(msg);
564      }
565          DumpMessage.dumpMsg(msg);
566          
567          System.out.println("\n\n*****************************************\n\n");
568
569          // 9. second certs only message
570          msg = createCertsOnlyMultiPartMessage(session);
571          System.out.println("creating message with certs-only part");
572          baos.reset();
573          msg.saveChanges();
574          msg.writeTo(baos);
575          bais = new ByteArrayInputStream(baos.toByteArray());
576          msg = new MimeMessage(session, bais);
577          if (PRINT_MESSAGES) {
578        printMessage(msg);
579      }
580          DumpMessage.dumpMsg(msg);
581          
582          System.out.println("\n\n*****************************************\n\n");
583
584          // 10. application/pkcs10 cert request message
585      msg = createPKCS10Message(session);
586      System.out.println("creating application/pkcs10 message...");
587          baos.reset();
588          msg.saveChanges();
589          msg.writeTo(baos);
590          bais = new ByteArrayInputStream(baos.toByteArray());
591          msg = new MimeMessage(session, bais);
592          if (PRINT_MESSAGES) {
593        printMessage(msg);
594      }
595          DumpMessage.dumpMsg(msg);
596          
597          System.out.println("\n\n*****************************************\n\n");
598
599          // 11. ending application/pkcs10 message where the request is in the second part
600          msg = createPKCS10MultiPartMessage(session);
601          System.out.println("creating message with pkcs10 part...");
602          baos.reset();
603          msg.saveChanges();
604          msg.writeTo(baos);
605          bais = new ByteArrayInputStream(baos.toByteArray());
606          msg = new MimeMessage(session, bais);
607          if (PRINT_MESSAGES) {
608        printMessage(msg);
609      }
610          DumpMessage.dumpMsg(msg);
611          
612          System.out.println("\n\n*****************************************\n\n");
613
614          // 12. compressed message 
615          msg = createCompressedMessage(session, multipart, (AlgorithmID)CMSAlgorithmID.zlib_compress.clone());
616          System.out.println("creating message with compressed data...");
617          baos.reset();
618          msg.saveChanges();
619          msg.writeTo(baos);
620          bais = new ByteArrayInputStream(baos.toByteArray());
621          msg = new MimeMessage(session, bais);
622          if (PRINT_MESSAGES) {
623        printMessage(msg);
624      }
625          DumpMessage.dumpMsg(msg);
626
627        } catch (Exception ex) {
628            ex.printStackTrace();
629            throw new RuntimeException(ex.toString());
630        }
631  }
632
633  /**
634   * Creates a MIME message container with the given subject for the given session.
635   * 
636   * @param session the mail sesion
637   * @param subject the subject of the message
638   *
639   * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
640   *
641   * @throws MessagingException if the message cannot be created
642   */
643  public Message createMessage(Session session, String subject) throws MessagingException {
644    MimeMessage msg = new MimeMessage(session);
645    msg.setFrom(new InternetAddress(from));
646        msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to, false));
647        msg.setSentDate(new Date());
648    msg.setSubject(subject);
649    return msg;
650  }
651  
652  /**
653   * Creates a simple plain (neither signed nor encrypted) message.
654   *
655   * @param session the mail session
656   * @param dataHandler the content of the message
657   * 
658   * @return the plain message
659   *
660   * @throws MessagingException if an error occurs when creating the message
661   */
662  public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException {
663
664    Message msg = createMessage(session, "IAIK-S/MIME: Plain message");
665    if (dataHandler != null) {
666      msg.setDataHandler(dataHandler);
667    } else {
668      msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n");
669    }
670    return msg;
671  }
672  
673  /**
674   * Creates a signed and encrypted message.
675   *
676   * @param session the mail session
677   * @param algorithm the content encryption algorithm to be used
678   * @param dataHandler the content of the message to be signed and encrypted
679   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
680   *                 (multipart/signed) signing
681   * 
682   * @return the signed and encrypted message
683   *
684   * @throws MessagingException if an error occurs when creating the message
685   */
686  public Message createSignedAndEncryptedMessage(Session session, AlgorithmID algorithm, DataHandler dataHandler, boolean implicit)
687    throws MessagingException {
688
689    String subject = null;
690    String text = null;
691    if (implicit) {
692      subject = "IAIK-S/MIME: Implicitly Signed and Encrypted";
693      text = "This message is implicitly signed and encrypted!\n\n\n";
694    } else {
695      subject = "IAIK-S/MIME: Explicitly Signed and Encrypted";
696      text = "This message is explicitly signed and encrypted!\n\n\n";
697    }
698    Message msg = createMessage(session, subject);
699
700    SignedContent sc = new SignedContent(implicit);
701    if (dataHandler != null) {
702      sc.setDataHandler(dataHandler);
703    } else {
704      sc.setText(text);
705    }
706    sc.setCertificates(signerCertificates);
707    try {
708      sc.addSigner(signerPrivateKey, 
709                   signerCertificate,
710                   (AlgorithmID)AlgorithmID.sha256.clone(),
711                   (AlgorithmID)AlgorithmID.dsaWithSHA256.clone());
712    } catch (NoSuchAlgorithmException ex) {
713      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
714    }
715
716    EncryptedContent ec = null;
717    if (algorithm.equals(AlgorithmID.aes256_CBC)) {
718      ec = new EncryptedContent(sc);
719    } else {
720      ec = new AuthEncryptedContent(sc);
721    }
722    // encrypt for the recipient
723    AlgorithmID keyEA = (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone();
724    // the key wrap algorithm to use:
725    AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
726    // the length of the key encryption key to be generated:
727    int kekLength = 256;
728    try {  
729      ec.addRecipient(recipientCertificate, keyEA, keyWrapAlg, kekLength);
730    } catch (SMimeException ex) {
731      throw new MessagingException("Error adding ESDH recipient: " + ex.getMessage());   
732    }    
733    // I want to be able to decrypt the message, too
734    ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
735    // set the encryption algorithm
736    try {
737      ec.setEncryptionAlgorithm(algorithm, -1);
738    } catch (NoSuchAlgorithmException ex) {
739      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
740    }   
741    msg.setContent(ec, ec.getContentType());
742    // let the EncryptedContent update some message headers
743    ec.setHeaders(msg);
744
745    return msg;
746  }
747  
748  /**
749   * Creates a signed message.
750   *
751   * @param session the mail session
752   * @param dataHandler the content of the message to be signed
753   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
754   *                 (multipart/signed) signing
755   * @param digestAlgorithm the digest algorithm to be used
756   * @param signatureAlgorithm the signature algorithm to be used                
757   * 
758   * @return the signed message
759   *
760   * @throws MessagingException if an error occurs when creating the message
761   */
762  public Message createSignedMessage(Session session, 
763                                     DataHandler dataHandler,
764                                     boolean implicit,
765                                     AlgorithmID digestAlgorithm,
766                                     AlgorithmID signatureAlgorithm)
767      throws MessagingException {
768
769    String subject = null;
770    StringBuffer buf = new StringBuffer();
771    
772    if (implicit) {
773      subject = "IAIK-S/MIME: Implicitly Signed";
774      buf.append("This message is implicitly signed!\n");
775      buf.append("You need an S/MIME aware mail client to view this message.\n");
776      buf.append("\n\n");
777    } else {
778      subject = "IAIK-S/MIME: Explicitly Signed";
779      buf.append("This message is explicitly signed!\n");
780      buf.append("Every mail client can view this message.\n");
781      buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
782      buf.append("\n\n");
783    }
784    
785    Message msg = createMessage(session, subject);
786    
787    SignedContent sc = new SignedContent(implicit);
788    if (dataHandler != null) {
789      sc.setDataHandler(dataHandler);
790    } else {
791      sc.setText(buf.toString());
792    }
793    sc.setCertificates(signerCertificates);
794
795    try {
796      sc.addSigner(signerPrivateKey, 
797                   signerCertificate,
798                   (AlgorithmID)digestAlgorithm.clone(),
799                   (AlgorithmID)signatureAlgorithm.clone());
800    } catch (NoSuchAlgorithmException ex) {
801      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
802    }
803
804    msg.setContent(sc, sc.getContentType());
805    // let the SignedContent update some message headers
806    sc.setHeaders(msg);
807    return msg;
808  }
809  
810  /**
811   * Creates an encrypted message.
812   *
813   * @param session the mail session
814   * @param contentEA the content encryption algorithm to be used
815   * @param keyLength the length of the secret content encryption key to be created and used
816   * @param keyEA the key encryption algorithm to be used
817   * @param keyWrapAlgorithm the key wrap algorithm to be used
818   * @param kekLength the length of the key encryption algorithm
819   * 
820   * @return the encrypted message
821   *
822   * @throws MessagingException if an error occurs when creating the message
823   */
824  public Message createEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength,
825    AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength)
826      throws MessagingException {
827    
828    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
829    AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone();
830    AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone();
831    
832    StringBuffer subject = new StringBuffer();
833    subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName());
834    if (keyLength > 0) {
835      subject.append("/"+keyLength);
836    }  
837    subject.append("]");
838    Message msg = createMessage(session, subject.toString());
839
840    EncryptedContent ec = new EncryptedContent();
841
842    StringBuffer buf = new StringBuffer();
843    buf.append("This is the encrypted content!\n");
844    buf.append("Content encryption algorithm: "+algorithm.getName());
845    buf.append("\n\n");
846
847    ec.setText(buf.toString());
848    
849    try {  
850      ec.addRecipient(recipientCertificate, keyAgreeAlg, keyWrapAlg, kekLength);
851    } catch (SMimeException ex) {
852      throw new MessagingException("Error adding ESDH recipient: " + ex.getMessage());   
853    }    
854    // I want to be able to decrypt the message, too
855    ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
856    try {
857      ec.setEncryptionAlgorithm(algorithm, keyLength);
858    } catch (NoSuchAlgorithmException ex) {
859      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
860    }    
861
862    msg.setContent(ec, ec.getContentType());
863    // let the EncryptedContent update some message headers
864    ec.setHeaders(msg);
865
866    return msg;
867  }
868  
869  /**
870   * Creates an authenticated encrypted message.
871   *
872   * @param session the mail session
873   * @param contentEA the content encryption algorithm to be used
874   * @param keyLength the length of the secret content encryption key to be created and used
875   * @param keyEA the key encryption algorithm to be used
876   * @param keyWrapAlgorithm the key wrap algorithm to be used
877   * @param kekLength the length of the key encryption algorithm 
878   * @return the encrypted message
879   *
880   * @throws MessagingException if an error occurs when creating the message
881   */
882  public Message createAuthEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength,
883    AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength)
884      throws MessagingException {
885    
886    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
887    AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone();
888    AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone();
889    
890    StringBuffer subject = new StringBuffer();
891    subject.append("IAIK-S/MIME: Authenticated Encrypted ["+algorithm.getName());
892    if (keyLength > 0) {
893      subject.append("/"+keyLength);
894    }  
895    subject.append("]");
896    Message msg = createMessage(session, subject.toString());
897
898    AuthEncryptedContent ec = new AuthEncryptedContent();
899
900    StringBuffer buf = new StringBuffer();
901    buf.append("This is the authenticated encrypted content!\n");
902    buf.append("Content encryption algorithm: "+algorithm.getName());
903    buf.append("\n\n");
904
905    ec.setText(buf.toString());
906    
907    try {  
908      ec.addRecipient(recipientCertificate, keyAgreeAlg, keyWrapAlg, kekLength);
909    } catch (SMimeException ex) {
910      throw new MessagingException("Error adding ESDH recipient: " + ex.getMessage());   
911    }    
912    // I want to be able to decrypt the message, too
913    ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
914    try {
915      ec.setEncryptionAlgorithm(algorithm, keyLength);
916    } catch (NoSuchAlgorithmException ex) {
917      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
918    }    
919
920    msg.setContent(ec, ec.getContentType());
921    // let the EncryptedContent update some message headers
922    ec.setHeaders(msg);
923
924    return msg;
925  }
926  
927  /**
928   * Creates a certs-only message.
929   *
930   * @param session the mail session
931   * 
932   * @return the certs-only message
933   *
934   * @throws MessagingException if an error occurs when creating the message
935   */
936  public Message createCertsOnlyMessage(Session session)
937      throws MessagingException {
938
939    Message msg = createMessage(session, "IAIK S/MIME: Certs-only message");
940    //use new content types
941    SMimeParameters.useNewContentTypes(true);
942    SignedContent sc = new SignedContent(true, SignedContent.CERTS_ONLY);
943    sc.setCertificates(signerCertificates);
944    msg.setContent(sc, sc.getContentType());
945    //set filename and attachment parameters
946    sc.setHeaders(msg);
947
948    return msg;
949  }
950  
951  
952  /**
953   * Creates a certs-only message where the certificate list is transferred as attachment.
954   *
955   * @param session the mail session
956   * 
957   * @return the certs-only message
958   *
959   * @throws MessagingException if an error occurs when creating the message
960   */
961  public Message createCertsOnlyMultiPartMessage(Session session) throws MessagingException {
962
963    MimeBodyPart mbp1 = new MimeBodyPart();
964        mbp1.setText("This is a test where the certs-only message is included in the second part!\n\n");
965
966    MimeBodyPart attachment = new MimeBodyPart();
967    //use new content types
968    SMimeParameters.useNewContentTypes(true);
969    SignedContent sc = new SignedContent(true, SignedContent.CERTS_ONLY);
970    sc.setCertificates(signerCertificates);
971    attachment.setContent(sc, sc.getContentType());
972    // let the SignedContent update some headers
973    sc.setHeaders(attachment);
974    Multipart mp = new MimeMultipart();
975    mp.addBodyPart(mbp1);
976    mp.addBodyPart(attachment);
977
978    Message msg = createMessage(session, "IAIK S/MIME: Certs-only multipart message");
979    msg.setContent(mp, mp.getContentType());
980    return msg;
981  }
982  
983   /**
984   * Creates a compressed message.
985   *
986   * @param session the mail session
987   * @param dataHandler the datahandler supplying the content to be compressed
988   * @param algorithm the compression algorithm to be used
989   * 
990   * @return the compressed message
991   *
992   * @throws MessagingException if an error occurs when creating the message
993   */
994  public Message createCompressedMessage(Session session, DataHandler dataHandler, AlgorithmID algorithm)
995      throws MessagingException {
996
997    String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]";
998    Message msg = createMessage(session, subject.toString());
999
1000    CompressedContent compressedContent = new CompressedContent();
1001    
1002    if (dataHandler == null) {
1003      StringBuffer buf = new StringBuffer();
1004      buf.append("This is the compressed content!\n");
1005      buf.append("Compression algorithm: "+algorithm.getName());
1006      buf.append("\n\n");
1007      compressedContent.setText(buf.toString());
1008    } else {
1009      compressedContent.setDataHandler(dataHandler);   
1010    }    
1011    
1012    try {
1013      compressedContent.setCompressionAlgorithm(algorithm);
1014    } catch (NoSuchAlgorithmException ex) {
1015      throw new MessagingException("Compression algorithm not supported: " + ex.getMessage());   
1016    }   
1017
1018    msg.setContent(compressedContent, compressedContent.getContentType());
1019    // let the CompressedContent update some message headers
1020    compressedContent.setHeaders(msg);
1021
1022    return msg;
1023  }
1024
1025  /**
1026   * Creates a PKCS#10 certificate request message.
1027   *
1028   * @param session the mail session
1029   * 
1030   * @return the PKCS#10 certificate request message
1031   *
1032   * @throws MessagingException if an error occurs when creating the message
1033   */
1034  public Message createPKCS10Message(Session session)
1035    throws MessagingException {
1036
1037    Message msg = createMessage(session, "IAIK-S/MIME: Certificate Request");
1038
1039    PKCS10Content pc = new PKCS10Content();
1040    CertificateRequest request = null;
1041    try {
1042       request = createCertificateRequest();
1043    } catch (PKCSException ex) {
1044       throw new MessagingException(ex.getMessage());
1045    }
1046    pc.setCertRequest(request);
1047    msg.setContent(pc, pc.getContentType());
1048    // let the PKCS10Content update some message headers
1049    pc.setHeaders(msg);
1050
1051    return msg;
1052  }
1053
1054
1055  private CertificateRequest createCertificateRequest() throws PKCSException {
1056    try {
1057      Name subject = new Name();
1058          subject.addRDN(ObjectID.commonName, firstName + " " + lastName);
1059          subject.addRDN(ObjectID.emailAddress, from);
1060          CertificateRequest certRequest;
1061      certRequest = new CertificateRequest(signerCertificate.getPublicKey(), subject);
1062          certRequest.sign((AlgorithmID)AlgorithmID.dsaWithSHA256.clone(), signerPrivateKey);
1063          certRequest.verify();
1064          return certRequest;
1065        } catch (Exception ex) {
1066          throw new PKCSException("Cannot create cert request: " + ex.getMessage());
1067        }
1068
1069  }
1070  
1071  /**
1072   * Creates a PKCS#10 message where the certificate request is transferred as attachment.
1073   *
1074   * @param session the mail session
1075   * 
1076   * @return the PKCS#10 certificate request message
1077   *
1078   * @throws MessagingException if an error occurs when creating the message
1079   */
1080  public Message createPKCS10MultiPartMessage(Session session) throws MessagingException {
1081
1082    MimeBodyPart mbp1 = new MimeBodyPart();
1083        mbp1.setText("This is a test where the request message is included in the second part!\n\n");
1084        // try to test an attachment
1085        // this demo attaches our homepage
1086    MimeBodyPart attachment = new MimeBodyPart();
1087    //use new content types
1088    SMimeParameters.useNewContentTypes(true);
1089    PKCS10Content pc = new PKCS10Content();
1090    CertificateRequest request = null;
1091    try {
1092      request = createCertificateRequest();
1093    } catch (PKCSException ex) {
1094      throw new MessagingException(ex.getMessage());
1095    }
1096    pc.setCertRequest(request);
1097    DataHandler pkcs10Handler = new DataHandler(pc, pc.getContentType());
1098    attachment.setDataHandler(pkcs10Handler);
1099    attachment.setDisposition("attachment");
1100    attachment.setFileName("smime.p10");
1101    Multipart mp = new MimeMultipart();
1102    mp.addBodyPart(mbp1);
1103    mp.addBodyPart(attachment);
1104
1105    Message msg = createMessage(session, "IAIK-S/MIME: Certificate Request multipart message");
1106    msg.setContent(mp, mp.getContentType());
1107    return msg;
1108  }
1109  
1110  /** 
1111   * Prints a dump of the given message to System.out.
1112   *
1113   * @param msg the message to be dumped to System.out
1114   *
1115   * @throws IOException if an I/O error occurs
1116   */
1117  static void printMessage(Message msg) throws IOException {
1118    System.out.println("------------------------------------------------------------------");
1119    System.out.println("Message dump: \n");
1120    try {
1121      msg.writeTo(System.out);
1122    } catch (MessagingException ex) {
1123      throw new IOException(ex.getMessage());   
1124    }    
1125    System.out.println("\n------------------------------------------------------------------");
1126  }  
1127
1128
1129  /**
1130   * The main method.
1131   */
1132  public static void main(String[] argv) throws IOException {
1133     
1134    DemoSMimeUtil.initDemos();
1135        (new SMimeV3Demo()).start();
1136    System.out.println("\nReady!");
1137    DemoUtil.waitKey();
1138  }
1139}