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