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/ecc/SMimeV4EccDemo.java 10    12.02.25 17:59 Dbratko $
029// $Revision: 10 $
030//
031
032package demo.smime.ecc;
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;
050
051import demo.DemoSMimeUtil;
052import demo.DemoUtil;
053import demo.cms.ecc.ECCDemoUtil;
054import demo.cms.ecc.keystore.CMSEccKeyStore;
055import demo.smime.DumpMessage;
056import iaik.asn1.structures.AlgorithmID;
057import iaik.cms.CMSAlgorithmID;
058import iaik.smime.AuthEncryptedContent;
059import iaik.smime.EncryptedContent;
060import iaik.smime.SMimeBodyPart;
061import iaik.smime.SMimeException;
062import iaik.smime.SMimeMultipart;
063import iaik.smime.SignedContent;
064import iaik.utils.KeyAndCertificate;
065import iaik.x509.X509Certificate;
066
067/**
068 * This class demonstrates the usage of the IAIK S/MIME implementation. It shows how to create
069 * signed and/or (authenticated) encrypted S/MIMEv4 messages using ECC keys and how to parse them and verify the signatures 
070 * and decrypt the content, respectively.
071 * <p>
072 * This demo uses several combinations of cryptographic algorithms that may not used in this
073 * way in practice. For a simple demos using only one set of algorithms see the {@link SimpleSMimeV4EcDemo SimpleSMimeV4EcDemo}
074 * and  {@link SimpleSMimeV4EdDemo SimpleSMimeV4EdDemo}.
075 * <p>
076 * Additionally to <code>iaik_cms.jar</code> you also must have 
077 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
078 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
079 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
080 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
081 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
082 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
083 * in your classpath.
084 * <p>
085 * To run this demo the following packages are required:
086 * <ul>
087 *    <li>
088 *       <code>iaik_cms.jar</code>
089 *    </li>
090 *    <li>
091 *       <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>).
092 *    </li>
093 *    <li>
094 *       <code>iaik_eccelerate.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">IAIK ECC Library</a>).
095 *    </li>   
096 *    <li>
097 *       <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
098 *    </li>   
099 *    <li>
100 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
101 *    </li> 
102 * </ul>
103 * 
104 */
105public class SMimeV4EccDemo {
106    
107  // whether to print dump all generates test messages to System.out
108  final static boolean PRINT_MESSAGES = false; 
109  
110  /**
111   * Cretaes a Multipart for the demo messages.
112   * 
113   * @return the Multipart
114   * 
115   * @throws MessagingException if an error occurs when creating the Mulitpart
116   */
117  static DataHandler createMultipart() throws MessagingException {
118    // Create a demo Multipart
119    MimeBodyPart mbp1 = new SMimeBodyPart();
120    mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
121    // attachment
122    MimeBodyPart attachment = new SMimeBodyPart();
123    attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
124    attachment.setFileName("test.html");
125
126    Multipart mp = new SMimeMultipart();
127    mp.addBodyPart(mbp1);
128    mp.addBodyPart(attachment);
129    DataHandler multipart = new DataHandler(mp, mp.getContentType());
130    return multipart;
131  }
132  
133
134  String firstName_ = "John";
135  String lastName_ = "SMime";
136  String to_ = "smimetest@iaik.tugraz.at";     // email recipient
137  String from_ = "smimetest@iaik.tugraz.at";   // email sender
138  String host_ = "mailhost";                   // name of the mailhost
139
140  /**
141   * Default constructor.
142   */
143  public SMimeV4EccDemo() {
144    
145    System.out.println();
146    System.out.println("********************************************************************************************");
147    System.out.println("*                                SMimeV4EccDemo demo                                       *");
148    System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIMEv4 messages) *");
149    System.out.println("********************************************************************************************");
150    System.out.println();
151
152  }
153  
154   
155  /**
156   * Starts the demo.
157   *
158   * @throws Exception if an error occurs
159   */
160  public void start() throws Exception {
161    
162    // get the default Session
163    Session session = DemoSMimeUtil.getSession();
164
165    // Create a demo Multipart
166    DataHandler multipart = createMultipart();
167    
168    // get signer key and certs
169    KeyAndCertificate[] signerKeyAndCerts = {
170        // P-256
171        new KeyAndCertificate(
172            CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA,
173                CMSEccKeyStore.SZ_256_SIGN),
174            CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA,
175                CMSEccKeyStore.SZ_256_SIGN)),
176        // P-384
177        new KeyAndCertificate(
178            CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA,
179                CMSEccKeyStore.SZ_384_SIGN),
180            CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA,
181                CMSEccKeyStore.SZ_384_SIGN)),
182        // P-521
183        new KeyAndCertificate(
184            CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA,
185                CMSEccKeyStore.SZ_521_SIGN),
186            CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA,
187                CMSEccKeyStore.SZ_521_SIGN)),
188        // ed25519  
189        new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519),
190                              CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519)),
191        // ed448  
192        new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448),
193                              CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448)),
194
195     };
196     
197     // the digest and signature algorithms to be used
198     AlgorithmID[][] digestAndSignatureAlgorithms = new AlgorithmID[][] {
199       { CMSAlgorithmID.sha256, CMSAlgorithmID.ecdsa_With_SHA256 },
200       { CMSAlgorithmID.sha384, CMSAlgorithmID.ecdsa_With_SHA384 },
201       { CMSAlgorithmID.sha512, CMSAlgorithmID.ecdsa_With_SHA512 },
202       { CMSAlgorithmID.sha512, CMSAlgorithmID.ed25519},
203       { CMSAlgorithmID.shake256Len, CMSAlgorithmID.ed448 },
204     };
205
206    
207     /**************************************************************************/
208     /*                                                                        */
209     /*                               Signing Demo                             */
210     /*                                                                        */
211     /**************************************************************************/
212     
213     final int DIGEST_ALG = 0;
214     final int SIGNATURE_ALG = 1;
215     for (int i = 0; i < digestAndSignatureAlgorithms.length; i++) {
216       AlgorithmID digestAlg = digestAndSignatureAlgorithms[i][DIGEST_ALG];
217       AlgorithmID signatureAlg = digestAndSignatureAlgorithms[i][SIGNATURE_ALG];
218       PrivateKey signerKey = signerKeyAndCerts[i].getPrivateKey();
219       X509Certificate[] signerCerts = signerKeyAndCerts[i].getCertificateChain();
220       System.out.println("Running signing demo for " + signatureAlg.getName());
221       startSigningDemo(session, 
222                        multipart, 
223                        digestAlg, 
224                        signatureAlg, 
225                        signerKey,
226                        signerCerts);
227    }
228     
229    // (authenticated) encryption demos
230     
231    // get recipient certs
232    X509Certificate[] recipientCerts = {
233       // P-256
234       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
235               CMSEccKeyStore.SZ_256_CRYPT_1)[0],
236       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
237               CMSEccKeyStore.SZ_256_CRYPT_2)[0],
238       // P-384
239       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
240               CMSEccKeyStore.SZ_384_CRYPT_1)[0],
241       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
242               CMSEccKeyStore.SZ_384_CRYPT_2)[0],
243       // P-521
244       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
245               CMSEccKeyStore.SZ_521_CRYPT_1)[0],
246       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH,
247               CMSEccKeyStore.SZ_521_CRYPT_2)[0],
248       // x25519  
249       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 
250               CMSEccKeyStore.SZ_X25519)[0],
251       // x448  
252       CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 
253               CMSEccKeyStore.SZ_X448)[0]
254
255    };
256     
257    //  the key encryption algorithms to be used
258    AlgorithmID[] keyEAs = {
259        AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme, 
260        AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme, 
261        AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme, 
262        AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme,
263        AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme, 
264    };
265    // key wrap algorithms
266    AlgorithmID[] keyWrapAlgs = {
267        CMSAlgorithmID.cms_aes128_wrap, 
268        CMSAlgorithmID.cms_aes192_wrap, 
269        CMSAlgorithmID.cms_aes256_wrap, 
270    };
271    // whether to encrypt or authenticated encrypt
272    boolean[] doAuthEncrypt = { false, true };
273    for (int h = 0; h < doAuthEncrypt.length; h++) {
274      boolean authEncrypt = doAuthEncrypt[h]; 
275      for (int i = 0; i < keyEAs.length; i++) {
276        AlgorithmID[] contentEAs;
277        AlgorithmID keyEA = keyEAs[i];
278        for (int j = 0; j < keyWrapAlgs.length; j++) {
279          AlgorithmID keyWrapAlg = keyWrapAlgs[j];
280          int kekLength;
281          int keyLength;
282          if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes192_wrap)) {
283            kekLength = 192;
284            keyLength = 192;
285            contentEAs = (authEncrypt) ? 
286                new AlgorithmID[]  { AlgorithmID.aes192_GCM, AlgorithmID.aes192_CCM } :
287                new AlgorithmID[]  { AlgorithmID.aes192_CBC };
288          } else if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes128_wrap)) {
289            kekLength = 128;
290            keyLength = 128;
291            contentEAs = (authEncrypt) ? 
292                new AlgorithmID[]  { AlgorithmID.aes128_GCM, AlgorithmID.aes128_CCM  } :
293                new AlgorithmID[]  { AlgorithmID.aes128_CBC };
294          } else {
295            kekLength = 256;
296            keyLength = 256;
297            contentEAs = (authEncrypt) ? 
298                new AlgorithmID[]  { AlgorithmID.aes256_GCM, AlgorithmID.aes256_CCM, AlgorithmID.chacha20Poly1305 } :
299                new AlgorithmID[]  { AlgorithmID.aes256_CBC };
300          }
301          
302          for (int k = 0; k < contentEAs.length; k++) {
303            
304            /**************************************************************************/
305            /*                                                                        */
306            /*                  (Authenticated) Encryption Demo                       */
307            /*                                                                        */
308            /**************************************************************************/
309            
310            AlgorithmID contentEA = contentEAs[k];
311            // in practice we may not create one message for recipients with different strength;
312            // however, for simplicity we use all recipients here
313            System.out.println("Running " + (authEncrypt ? "authenticated" : "") + "encryption demo for " + 
314                                keyEA.getName() + " with " + keyWrapAlg.getName() +" and " + contentEA.getName());
315            startEncryptionDemo(session,
316                                contentEA, 
317                                keyLength, 
318                                keyEA,
319                                keyWrapAlg,
320                                kekLength,
321                                authEncrypt, 
322                                recipientCerts);
323            
324            
325            /**************************************************************************/
326            /*                                                                        */
327            /*             Signing and (Authenticated) Encryption Demo                */
328            /*                                                                        */
329            /**************************************************************************/
330            
331            for (int l = 0; l < digestAndSignatureAlgorithms.length; l++) {
332              AlgorithmID digestAlg = digestAndSignatureAlgorithms[l][DIGEST_ALG];
333              AlgorithmID signatureAlg = digestAndSignatureAlgorithms[l][SIGNATURE_ALG];
334              PrivateKey signerKey = signerKeyAndCerts[l].getPrivateKey();
335              X509Certificate[] signerCerts = signerKeyAndCerts[l].getCertificateChain();
336              System.out.println("Running signing and " + (authEncrypt ? "authenticated" : "") + "encryption and demo for " + signatureAlg.getName());
337              startSigningAndEncryptionDemo(session,
338                  multipart,
339                  digestAlg,
340                  signatureAlg,
341                  contentEA, 
342                  keyLength, 
343                  keyEA,
344                  keyWrapAlg,
345                  kekLength,
346                  authEncrypt,  
347                  signerKey, 
348                  signerCerts,
349                  recipientCerts);
350            }
351          }
352        }
353      }
354    } 
355  } 
356 
357  /**
358   * Starts the signing demo.
359   * 
360   * @param session the mail session
361   * @param multipart the multipart content of the message to be signed
362   * @param digestAlgorithm the digest algorithm to be used
363   * @param signatureAlgorithm the signature algorithm to be used 
364   * @param signerKey the private key of the signer
365   * @param signerCerts the certificate chain of the signer     
366   *
367   * @throws Exception if an error occurs
368   */
369  public void startSigningDemo(Session session,
370                               DataHandler multipart, 
371                               AlgorithmID digestAlgorithm, 
372                               AlgorithmID signatureAlgorithm,
373                               PrivateKey signerKey, 
374                               X509Certificate[] signerCerts) throws Exception {
375    
376    Message msg;    // the message to send
377    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
378    ByteArrayInputStream bais;  // we read from a stream
379
380    // This is an explicitly signed message
381    System.out.println("creating explicitly signed message...");
382    msg = createSignedMessage(session, multipart, false, digestAlgorithm, signatureAlgorithm, signerKey, signerCerts);
383    baos.reset();
384    msg.saveChanges();
385    msg.writeTo(baos);
386    bais = new ByteArrayInputStream(baos.toByteArray());
387    msg = new MimeMessage(session, bais);
388    if (PRINT_MESSAGES) {
389      printMessage(msg);
390    }
391    DumpMessage.dumpMsg(msg);
392    
393    System.out.println("\n\n*****************************************\n\n");
394
395
396    // This is an implicitly signed message
397    System.out.println("creating implicitly signed message...");
398    msg = createSignedMessage(session, multipart, true, digestAlgorithm, signatureAlgorithm, signerKey, signerCerts);
399    baos.reset();
400    msg.saveChanges();
401    msg.writeTo(baos);
402    bais = new ByteArrayInputStream(baos.toByteArray());
403    msg = new MimeMessage(session, bais);
404    if (PRINT_MESSAGES) {
405      printMessage(msg);
406    }
407    DumpMessage.dumpMsg(msg);
408    
409    System.out.println("\n\n*****************************************\n\n");
410
411  }
412  
413  /**
414   * Starts the (maybe authenticated) encryption demo.
415   * 
416   * @param session the mail session
417   * @param contentEA the content encryption algorithm to be used
418   * @param keyLength the length of the secret content encryption key to be created and used
419   * @param keyEA the (key agreement) algorithm to use for creating a shared secret
420   *              key encryption key for encrypting the symmetric key
421   *              (e.g. AlgorithmID.esdhKeyAgreement)
422   * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping)
423   *                   the content-encryption key with the shared key-encryption
424   *                   created according to the requested key agreement protocol
425   * @param kekLength the length of the shared key encryption key to be generated
426   * @param authEncrypt whether to create an authenticated encrypted message 
427   * @param recipientCerts the certificates of the recipients       
428   *
429   * @throws Exception if an error occurs
430   */
431  public void startEncryptionDemo(Session session,
432                                  AlgorithmID contentEA, 
433                                  int keyLength, 
434                                  AlgorithmID keyEA,
435                                  AlgorithmID keyWrapAlgorithm,
436                                  int kekLength,
437                                  boolean authEncrypt, 
438                                  X509Certificate[] recipientCerts) throws Exception {
439
440 
441    Message msg;    // the message to send
442    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
443    ByteArrayInputStream bais;  // we read from a stream
444
445    System.out.println("creating encrypted message " + contentEA.getName());
446    msg = createEncryptedMessage(session, (AlgorithmID)contentEA.clone(), keyLength,
447      (AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlgorithm.clone(), kekLength, authEncrypt,
448       recipientCerts);
449
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.dumpEncryptedMessage(msg);
459    
460    System.out.println("\n\n*****************************************\n\n");
461
462    
463  }
464  
465  
466  /**
467   * Starts the signing + (maybe authenticated) encryption demo.
468   * 
469   * @param session the mail session
470   * @param multipart the Multipart content to be signed and encrypted
471   * @param digestAlgorithm the digest algorithm to be used for signing
472   * @param signatureAlgorithm the signature algorithm to be used 
473   * @param contentEA the content encryption algorithm to be used
474   * @param keyLength the length of the secret content encryption key to be created and used
475   * @param keyEA the (key agreement) algorithm to use for creating a shared secret
476   *              key encryption key for encrypting the symmetric key
477   *              (e.g. AlgorithmID.esdhKeyAgreement)
478   * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping)
479   *                   the content-encryption key with the shared key-encryption
480   *                   created according to the requested key agreement protocol
481   * @param kekLength the length of the shared key encryption key to be generated
482   * @param authEncrypt whether to create an authenticated encrypted message 
483   * @param signerKey the private key of the signer
484   * @param signerCerts the certificate chain of the signer 
485   * @param recipientCerts the certificates of the recipients       
486   *
487   * @throws Exception if an error occurs
488   */
489  public void startSigningAndEncryptionDemo(Session session,
490                                            DataHandler multipart,
491                                            AlgorithmID digestAlgorithm,
492                                            AlgorithmID signatureAlgorithm,
493                                            AlgorithmID contentEA, 
494                                            int keyLength, 
495                                            AlgorithmID keyEA,
496                                            AlgorithmID keyWrapAlgorithm,
497                                            int kekLength,
498                                            boolean authEncrypt, 
499                                            PrivateKey signerKey, 
500                                            X509Certificate[] signerCerts,
501                                            X509Certificate[] recipientCerts) throws Exception {
502
503 
504    Message msg;    // the message to send
505    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
506    ByteArrayInputStream bais;  // we read from a stream
507
508   // Create an implicitly signed and authenticated encrypted message with attachment
509    System.out.println("creating implicitly signed and authenticated encrypted message  " + contentEA.getName());
510    msg = createSignedAndEncryptedMessage(session, digestAlgorithm, signatureAlgorithm, contentEA, keyLength,
511        keyEA, keyWrapAlgorithm, kekLength, multipart, true, authEncrypt,
512        signerKey, signerCerts, recipientCerts);
513    baos.reset();
514    msg.saveChanges();
515    msg.writeTo(baos);
516    bais = new ByteArrayInputStream(baos.toByteArray());
517    msg = new MimeMessage(session, bais);
518    if (PRINT_MESSAGES) {
519      printMessage(msg);
520    }
521    DumpMessage.dumpEncryptedMessage(msg);
522    
523    System.out.println("\n\n*****************************************\n\n");
524
525    // Create an explicitly signed and authenticated encrypted message with attachment
526    System.out.println("creating explicitly signed and authenticated encrypted message " + contentEA.getName());
527    msg = createSignedAndEncryptedMessage(session, digestAlgorithm, signatureAlgorithm, contentEA, keyLength,
528        keyEA, keyWrapAlgorithm, kekLength, multipart, true, authEncrypt,
529        signerKey, signerCerts, recipientCerts);
530    baos.reset();
531    msg.saveChanges();
532    msg.writeTo(baos);
533    bais = new ByteArrayInputStream(baos.toByteArray());
534    msg = new MimeMessage(session, bais);
535    if (PRINT_MESSAGES) {
536      printMessage(msg);
537    }
538    DumpMessage.dumpEncryptedMessage(msg);
539
540  }
541  
542  /**
543   * Creates a MIME message container with the given subject for the given session.
544   * 
545   * @param session the mail sesion
546   * @param subject the subject of the message
547   *
548   * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
549   *
550   * @throws MessagingException if the message cannot be created
551   */
552  public Message createMessage(Session session, String subject) throws MessagingException {
553    MimeMessage msg = new MimeMessage(session);
554    msg.setFrom(new InternetAddress(from_));
555        msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to_, false));
556        msg.setSentDate(new Date());
557    msg.setSubject(subject);
558    return msg;
559  }
560  
561 
562  /**
563   * Creates a signed message.
564   *
565   * @param session the mail session
566   * @param dataHandler the content of the message to be signed
567   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
568   *                 (multipart/signed) signing
569   * @param digestAlgorithm the digest algorithm to be used
570   * @param signatureAlgorithm the signature algorithm to be used 
571   * @param signerKey the private key of the signer
572   * @param signerCertificates the certificate chain of the signer               
573   * 
574   * @return the signed message
575   *
576   * @throws MessagingException if an error occurs when creating the message
577   */
578  public Message createSignedMessage(Session session, 
579                                     DataHandler dataHandler,
580                                     boolean implicit,
581                                     AlgorithmID digestAlgorithm,
582                                     AlgorithmID signatureAlgorithm,
583                                     PrivateKey signerKey,
584                                     X509Certificate[] signerCertificates)
585      throws MessagingException {
586
587    String subject = null;
588    StringBuffer buf = new StringBuffer();
589    
590    if (implicit) {
591      subject = "IAIK-S/MIME: Implicitly Signed";
592      buf.append("This message is implicitly signed!\n");
593      buf.append("You need an S/MIME aware mail client to view this message.\n");
594      buf.append("\n\n");
595    } else {
596      subject = "IAIK-S/MIME: Explicitly Signed";
597      buf.append("This message is explicitly signed!\n");
598      buf.append("Every mail client can view this message.\n");
599      buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
600      buf.append("\n\n");
601    }
602    
603    Message msg = createMessage(session, subject);
604    
605    SignedContent sc = new SignedContent(implicit);
606    if (dataHandler != null) {
607      sc.setDataHandler(dataHandler);
608    } else {
609      sc.setText(buf.toString());
610    }
611    sc.setCertificates(signerCertificates);
612
613    try {
614      sc.addSigner(signerKey, 
615                   signerCertificates[0],
616                   (AlgorithmID)digestAlgorithm.clone(),
617                   (AlgorithmID)signatureAlgorithm.clone());
618    } catch (NoSuchAlgorithmException ex) {
619      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
620    }
621
622    msg.setContent(sc, sc.getContentType());
623    // let the SignedContent update some message headers
624    sc.setHeaders(msg);
625    return msg;
626  }
627  
628  /**
629   * Creates an (maybe authenticated) encrypted message.
630   *
631   * @param session the mail session
632   * @param contentEA the content encryption algorithm to be used
633   * @param keyLength the length of the secret content encryption key to be created and used
634   * @param keyEA the (key agreement) algorithm to use for creating a shared secret
635   *              key encryption key for encrypting the symmetric key
636   *              (e.g. AlgorithmID.esdhKeyAgreement)
637   * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping)
638   *                   the content-encryption key with the shared key-encryption
639   *                   created according to the requested key agreement protocol
640   * @param kekLength the length of the shared key encryption key to be generated
641   * @param authEncrypt whether to create an authenticated encrypted message
642   * @param recipientCerts the certificates of the recipients
643   * 
644   * @return the (maybe authenticated) encrypted message
645   *
646   * @throws MessagingException if an error occurs when creating the message
647   */
648  public Message createEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength,
649    AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength, boolean authEncrypt,
650    X509Certificate[] recipientCerts)
651      throws MessagingException {
652    
653    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
654    AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone();
655    AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone();
656    
657    StringBuffer subject = new StringBuffer();
658    subject.append("IAIK-S/MIME: " + (authEncrypt ? "Authenticated " : "") + " Encrypted ["+algorithm.getName());
659    if (keyLength > 0) {
660      subject.append("/"+keyLength);
661    }  
662    subject.append("]");
663    Message msg = createMessage(session, subject.toString());
664
665    EncryptedContent ec = null;
666    if (authEncrypt) {
667      ec = new AuthEncryptedContent();
668    } else {
669      ec = new EncryptedContent();
670    }
671
672    StringBuffer buf = new StringBuffer();
673    buf.append("This is the " + (authEncrypt ? "authenticated " : "") + " encrypted content!\n");
674    buf.append("Content encryption algorithm: "+algorithm.getName());
675    buf.append("\n\n");
676
677    ec.setText(buf.toString());
678    
679    // we use ephemeral-static ECDH
680    for (int i = 0; i < recipientCerts.length; i++) {
681      try {
682        ec.addRecipient(recipientCerts[i], keyAgreeAlg, keyWrapAlg, kekLength);
683      } catch (SMimeException ex) {
684        throw new MessagingException("Error adding ECDH recipient: " + ex.getMessage());   
685      } 
686    }  
687   
688    try {
689      ec.setEncryptionAlgorithm(algorithm, keyLength);
690    } catch (NoSuchAlgorithmException ex) {
691      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
692    }    
693
694    msg.setContent(ec, ec.getContentType());
695    // let the EncryptedContent update some message headers
696    ec.setHeaders(msg);
697
698    return msg;
699  }
700  
701  /**
702   * Creates a signed and (maybe authenticated) encrypted message.
703   *
704   * @param session the mail session
705   * @param digestAlgorithm the digest algorithm to be used for signing
706   * @param signatureAlgorithm the signature algorithm to be used 
707   * @param contentEA the content encryption algorithm to be used
708   * @param keyLength the length of the secret content encryption key to be created and used
709   * @param keyEA the (key agreement) algorithm to use for creating a shared secret
710   *              key encryption key for encrypting the symmetric key
711   *              (e.g. AlgorithmID.esdhKeyAgreement)
712   * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping)
713   *                   the content-encryption key with the shared key-encryption
714   *                   created according to the requested key agreement protocol
715   * @param kekLength the length of the shared key encryption key to be generated
716   * @param dataHandler the content of the message to be signed and encrypted
717   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
718   *                 (multipart/signed) signing
719   * @param authEncrypt whether to create an authenticated encrypted message 
720   * @param signerKey the private key of the signer
721   * @param signerCerts the certificate chain of the signer 
722   * @param recipientCerts the certificates of the recipients      
723   * 
724   * @return the signed and (maybe authenticated) encrypted message
725   *
726   * @throws MessagingException if an error occurs when creating the message
727   */
728  public Message createSignedAndEncryptedMessage(Session session,
729    AlgorithmID digestAlgorithm, AlgorithmID signatureAlgorithm,
730    AlgorithmID contentEA, int keyLength,
731    AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength,
732    DataHandler dataHandler, boolean implicit, boolean authEncrypt,
733    PrivateKey signerKey, X509Certificate[] signerCerts,
734    X509Certificate[] recipientCerts)
735    throws MessagingException {
736
737    String subject = null;
738    String text = null;
739    if (implicit) {
740      subject = "IAIK-S/MIME: Implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted";
741      text = "This message is implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted!\n\n\n";
742    } else {
743      subject = "IAIK-S/MIME: explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted";
744      text = "This message is explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted!\n\n\n";
745    }
746    Message msg = createMessage(session, subject);
747
748    SignedContent sc = new SignedContent(implicit);
749    if (dataHandler != null) {
750      sc.setDataHandler(dataHandler);
751    } else {
752      sc.setText(text);
753    }
754    sc.setCertificates(signerCerts);
755    try {
756      sc.addSigner(signerKey, 
757                   signerCerts[0],
758                   (AlgorithmID)digestAlgorithm.clone(),
759                   (AlgorithmID)signatureAlgorithm.clone());
760    } catch (NoSuchAlgorithmException ex) {
761      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
762    }
763
764    EncryptedContent ec = null;
765    if (authEncrypt) {
766      ec = new AuthEncryptedContent(sc);
767    } else {
768      ec = new EncryptedContent(sc);
769    }
770
771    
772    AlgorithmID algorithm = (AlgorithmID)contentEA.clone();
773    AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone();
774    AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone();
775    
776    // we use ephemeral-static DH
777    for (int i = 0; i < recipientCerts.length; i++) {
778      try {
779        ec.addRecipient(recipientCerts[i], keyAgreeAlg, keyWrapAlg, kekLength);
780      } catch (SMimeException ex) {
781        throw new MessagingException("Error adding ESDH recipient: " + ex.getMessage());   
782      } 
783    }  
784 
785    // set the encryption algorithm
786    try {
787      ec.setEncryptionAlgorithm(algorithm, keyLength);
788    } catch (NoSuchAlgorithmException ex) {
789      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
790    }   
791    msg.setContent(ec, ec.getContentType());
792    // let the EncryptedContent update some message headers
793    ec.setHeaders(msg);
794
795    return msg;
796  }
797  
798  
799
800  /** 
801   * Prints a dump of the given message to System.out.
802   *
803   * @param msg the message to be dumped to System.out
804   *
805   * @throws IOException if an I/O error occurs
806   */
807  static void printMessage(Message msg) throws IOException {
808    System.out.println("------------------------------------------------------------------");
809    System.out.println("Message dump: \n");
810    try {
811      msg.writeTo(System.out);
812    } catch (MessagingException ex) {
813      throw new IOException(ex.getMessage());   
814    }    
815    System.out.println("\n------------------------------------------------------------------");
816  }  
817  
818
819  /**
820   * The main method.
821   */
822  public static void main(String[] argv) throws Exception {
823     
824    DemoSMimeUtil.initDemos();
825    //  add ECC provider    
826    ECCDemoUtil.installIaikEccProvider();
827    
828        (new SMimeV4EccDemo()).start();
829    System.out.println("\nReady!");
830    DemoUtil.waitKey();
831  }
832}