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/cms/ecc/EdDHAuthEnvelopedDataDemo.java 5     12.02.25 17:58 Dbratko $
059    // $Revision: 5 $
060    //
061    
062    
063    package demo.cms.ecc;
064    
065    import iaik.asn1.CodingException;
066    import iaik.asn1.ObjectID;
067    import iaik.asn1.structures.AlgorithmID;
068    import iaik.asn1.structures.Attribute;
069    import iaik.cms.AuthEnvelopedData;
070    import iaik.cms.AuthEnvelopedDataOutputStream;
071    import iaik.cms.AuthEnvelopedDataStream;
072    import iaik.cms.CMSAlgorithmID;
073    import iaik.cms.CMSException;
074    import iaik.cms.CertificateIdentifier;
075    import iaik.cms.ContentInfo;
076    import iaik.cms.ContentInfoOutputStream;
077    import iaik.cms.ContentInfoStream;
078    import iaik.cms.EncryptedContentInfo;
079    import iaik.cms.EncryptedContentInfoStream;
080    import iaik.cms.IssuerAndSerialNumber;
081    import iaik.cms.KeyAgreeRecipientInfo;
082    import iaik.cms.KeyIdentifier;
083    import iaik.cms.RecipientInfo;
084    import iaik.cms.RecipientKeyIdentifier;
085    import iaik.cms.attributes.CMSContentType;
086    import iaik.security.random.SecRandom;
087    import iaik.utils.Util;
088    import iaik.x509.X509Certificate;
089    
090    import java.io.ByteArrayInputStream;
091    import java.io.ByteArrayOutputStream;
092    import java.io.IOException;
093    import java.io.InputStream;
094    import java.security.InvalidKeyException;
095    import java.security.Key;
096    import java.security.NoSuchAlgorithmException;
097    import java.security.PrivateKey;
098    import java.security.SecureRandom;
099    
100    import javax.crypto.SecretKey;
101    
102    import demo.DemoUtil;
103    import demo.cms.ecc.keystore.CMSEccKeyStore;
104    
105    
106    /**
107     * Demonstrates the usage of class {@link iaik.cms.AuthEnvelopedDataStream},
108     * {@link iaik.cms.AuthEnvelopedData} and {@link iaik.cms.AuthEnvelopedDataOutputStream} 
109     * for authenticated encrypting data with the CMS content type AuthEnvelopedData using the 
110     * Elliptic Curve Diffie-Hellman (ECDH) key agreement algorithm with curve25519 and
111     * curve448 according to <a href = "http://www.ietf.org/rfc/rfc5083.txt" target="_blank">RFC 5083</a>
112     * and <a href = "http://www.ietf.org/rfc/rfc8418.txt" target="_blank">RFC 8418</a>.
113     * <p>
114     * This demo uses the AES-CCM and AES-GCM authenticated encryption algorithms
115     * as specified by <a href = "http://www.ietf.org/rfc/rfc5084.txt" target="_blank">RFC 5084</a>.
116     * The demo creates an AuthEnvelopedData object and subsequently shows several
117     * ways that may be used for decrypting the content and verifying the message
118     * authentication code for some particular recipient.
119     * <br>
120     * Since AES-CCM and AES-GCM are not implemented by IAIK-JCE versions prior 3.17, this demo
121     * at least may require IAIK-JCE 3.17 as cryptographic service provider. 
122     * <p>
123     * Any keys/certificates required for this demo are read from a keystore
124     * file "cmsecc.keystore" located in your current working directory. If
125     * the keystore file does not exist you can create it by running the
126     * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
127     * program. 
128     * <p>
129     * Additionally to <code>iaik_cms.jar</code> you also must have 
130     * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
131     * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
132     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
133     * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
134     * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
135     * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
136     * in your classpath.
137     * 
138     * @see iaik.cms.AuthEnvelopedDataStream
139     * @see iaik.cms.AuthEnvelopedData
140     * @see iaik.cms.AuthEnvelopedDataOutputStream
141     * @see iaik.cms.RecipientInfo
142     * @see iaik.cms.KeyAgreeRecipientInfo
143     */
144    public class EdDHAuthEnvelopedDataDemo {
145    
146      //certificate of x25519 user
147      X509Certificate x25519User;
148      // private key of x25519 user
149      PrivateKey x25519User_pk;
150      // certificate of x448 user 
151      X509Certificate x448User;
152      // private key of x448 user 
153      PrivateKey x448User_pk;
154      
155      // secure random number generator
156      SecureRandom random;
157    
158      /**
159       * Setup the demo certificate chains.
160       *
161       * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore")
162       * file which has to be located in your current working directory and may be
163       * created by running {@link demo.keystore.SetupCMSKeyStore
164       * SetupCMSKeyStore}.
165       *
166       * @throws IOException if an file read error occurs
167       */
168      public EdDHAuthEnvelopedDataDemo() throws IOException {
169        
170        System.out.println();
171        System.out.println("***************************************************************************************************");
172        System.out.println("*                                    EdDHAuthEnvelopedDataDemo                                    *");
173        System.out.println("* (shows the usage of the CMS AuthEnvelopedData type implementation with curve25519 and curve448) *");
174        System.out.println("***************************************************************************************************");
175        System.out.println();
176        
177        // get keys and certificate from the demo keystore
178        x25519User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519)[0];
179        x25519User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519);
180        x448User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448)[0];
181        x448User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448);
182        random = SecRandom.getDefault();
183        
184      }
185    
186    
187      /**
188       * Creates a CMS <code>AuthEnvelopedDataStream</code> message.
189       *
190       * @param message the message to be authenticated-enveloped, as byte representation
191       * @param keyEA the key encryption (key agreement) algorithm used for creating 
192       *              a shared key encryption key for encrypting the secret content
193       *              encryption key with it
194       * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
195       *                   the content encryption key
196       * @param kekLength the length of the key encryption key to be created for
197       *                  encrypting the content encryption key with it     
198       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
199       * 
200       * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
201       * 
202       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
203       *                          be created
204       * @throws IOException if an I/O error occurs
205       */
206      public byte[] createAuthEnvelopedDataStream(byte[] message, 
207                                                  AlgorithmID keyEA, 
208                                                  AlgorithmID keyWrapAlg,
209                                                  int kekLength, 
210                                                  AlgorithmID contentAuthEncAlg)
211        throws CMSException, IOException {
212    
213        // we are testing the stream interface
214        ByteArrayInputStream is = new ByteArrayInputStream(message);
215        // create a new AuthEnvelopedData object 
216        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is, contentAuthEncAlg);
217        
218        if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
219            contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
220            contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
221          // for aes-ccm we need to know the data input length in advance
222          authEnvelopedData.setInputLength(message.length);
223        }
224        
225        //  create some authenticated attributes
226        try {
227          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
228          authEnvelopedData.setAuthenticatedAttributes(attributes);
229        } catch (Exception ex) {
230          throw new CMSException("Error creating attribute: " + ex.toString());   
231        }
232    
233        // create the recipient infos
234        RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
235        // specify the recipients of the encrypted message
236        authEnvelopedData.setRecipientInfos(recipients);
237    
238        // return the AuthEnvelopedDate as BER encoded byte array with block size 16
239        // (just for testing; in real application we will use a proper blocksize,
240        //  e.g. 2048, 4096,..)
241        authEnvelopedData.setBlockSize(16);
242        // wrap into ContentInfo
243        ContentInfoStream contentInfo = new ContentInfoStream(authEnvelopedData);
244        ByteArrayOutputStream os = new ByteArrayOutputStream();
245        contentInfo.writeTo(os);
246        return os.toByteArray();
247      }
248      
249      /**
250       * Creates a CMS <code>AuthEnvelopedData</code> message using the 
251       * {@link iaik.cms.AuthEnvelopedDataOutputStream AuthEnvelopedDataOutputStream}
252       * class.
253       *
254       * @param message the message to be authenticated-enveloped, as byte representation
255       * @param keyEA the key encryption (key agreement) algorithm used for creating 
256       *              a shared key encryption key for encrypting the secret content
257       *              encryption key with it
258       * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
259       *                   the content encryption key
260       * @param kekLength the length of the key encryption key to be created for
261       *                  encrypting the content encryption key with it    
262       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
263       * 
264       * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
265       * 
266       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
267       *                          be created
268       * @throws IOException if an I/O error occurs
269       */
270      public byte[] createAuthEnvelopedDataOutputStream(byte[] message, 
271                                                        AlgorithmID keyEA, 
272                                                        AlgorithmID keyWrapAlg,
273                                                        int kekLength,
274                                                        AlgorithmID contentAuthEncAlg)
275        throws CMSException, IOException {
276    
277        //  a stream from which to read the data to be encrypted
278        ByteArrayInputStream is = new ByteArrayInputStream(message);
279        
280        // the stream to which to write the AuthEnvelopedData
281        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
282    
283        //  wrap AuthEnvelopedData into a ContentInfo 
284        ContentInfoOutputStream contentInfoStream = 
285          new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
286       
287        // create a new AuthEnvelopedData object 
288        AuthEnvelopedDataOutputStream authEnvelopedData = 
289            new AuthEnvelopedDataOutputStream(contentInfoStream, contentAuthEncAlg);
290        
291        if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
292            contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
293            contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
294          // for aes-ccm we need to know the data input length in advance
295          authEnvelopedData.setInputLength(message.length);
296        }
297        
298        //  create some authenticated attributes
299        try {
300          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
301          authEnvelopedData.setAuthenticatedAttributes(attributes);
302        } catch (Exception ex) {
303          throw new CMSException("Error creating attribute: " + ex.toString());   
304        }
305    
306        // create the recipient infos
307        RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
308        // specify the recipients of the encrypted message
309        authEnvelopedData.setRecipientInfos(recipients);
310    
311        int blockSize = 16; // in real world we would use a block size like 2048
312        //  write in the data to be encrypted
313        byte[] buffer = new byte[blockSize];
314        int bytesRead;
315        while ((bytesRead = is.read(buffer)) != -1) {
316          authEnvelopedData.write(buffer, 0, bytesRead);
317        }
318        
319        // closing the stream finishes encryption and closes the underlying stream
320        authEnvelopedData.close();
321        return resultStream.toByteArray();
322      }
323      
324    
325      /**
326       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
327       * the recipient identified by its index into the recipientInfos field and verifies
328       * the message authentication code.
329       * <p>
330       * This way of decrypting the content may be used for any type of RecipientInfo
331       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
332       * know at what index of the recipientInfo field the RecipientInfo for the 
333       * particular recipient in mind can be found. If the recipient in mind uses
334       * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
335       * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
336       * keys for more than only one recipient; since the recipientInfoIndex only
337       * specifies the RecipientInfo but not the encrypted content encryption key 
338       * -- if there are more than only one -- repeated decryption runs may be
339       * required as long as the decryption process completes successfully.
340       *
341       * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
342       * @param key the key to decrypt the message
343       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
344       *                        to which the specified key belongs
345       *
346       * @return the recovered message, as byte array
347       * @throws CMSException if the message cannot be recovered or MAC verification fails
348       * @throws IOException if a stream read/write error occurs
349       */
350      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
351        throws CMSException, IOException {
352    
353        // create the AuthEnvelopedData object from a BER encoded byte array
354        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
355        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
356    
357        System.out.println("Information about the authenticated encrypted data:");
358        EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
359        System.out.println("Content type: "+eci.getContentType().getName());
360        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
361    
362        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
363        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
364        
365        // for demonstration purposes we only look one time for all recipients included:
366        if (recipientInfoIndex == 0) {
367          int k = 0;
368          for (int i=0; i<recipients.length; i++) {
369            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
370            for (int j = 0; j < recipientIDs.length; j++) {
371              System.out.println("Recipient "+(++k)+":");
372              System.out.println(recipientIDs[j]);
373            }   
374          }
375        }
376        // decrypt the message for the first recipient and verify mac
377        try {
378          authEnvelopedData.setupCipher(key, recipientInfoIndex);
379          InputStream decrypted = authEnvelopedData.getInputStream();
380          ByteArrayOutputStream os = new ByteArrayOutputStream();
381          Util.copyStream(decrypted, os, null);
382          byte[] content = os.toByteArray();
383    
384          // get authenticated attributes
385          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
386          if (contentTypeAttribute != null) {
387            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
388            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
389          }
390          
391          return content;
392        } catch (InvalidKeyException ex) {
393          throw new CMSException("Private key error: "+ex.toString());
394        } catch (NoSuchAlgorithmException ex) {
395          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
396        } catch (CodingException ex) {
397          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
398        }
399      }
400      
401      /**
402       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
403       * the recipient identified by recipient identifier and verifies the message 
404       * authentication code.
405       * <p>
406       * This way of decrypting the content may be used for any type of RecipientInfo
407       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
408       * recipient in mind is identified by its recipient identifier.
409       *
410       * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
411       * @param key the key to decrypt the message
412       * @param recipientID the recipient identifier uniquely identifying the key of the
413       *        recipient
414       *
415       * @return the recovered message, as byte array
416       * @throws CMSException if the message cannot be recovered
417       * @throws IOException if a stream read/write error occurs
418       */
419      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
420        throws CMSException, IOException {
421    
422        // create the AuthEnvelopedData object from a DER encoded byte array
423        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
424        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
425    
426        System.out.println("Information about the encrypted data:");
427        EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
428        System.out.println("Content type: "+eci.getContentType().getName());
429        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
430        
431        // get the right RecipientInfo
432        System.out.println("\nSearch for RecipientInfo:");
433        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
434        if (recipient != null) {
435          System.out.println("RecipientInfo: " + recipient);   
436        } else {
437          throw new CMSException("No recipient with ID: " + recipientID);
438        }    
439        // decrypt the content encryption key and the content; verify mac
440        try {
441          System.out.println("Decrypt encrypted content encryption key...");
442          SecretKey cek = recipient.decryptKey(key, recipientID);
443          System.out.println("Decrypt content with decrypted content encryption key...");
444          authEnvelopedData.setupCipher(cek);
445          InputStream decrypted = authEnvelopedData.getInputStream();
446          ByteArrayOutputStream os = new ByteArrayOutputStream();
447          Util.copyStream(decrypted, os, null);
448          byte[] content = os.toByteArray();
449    
450          // get authenticated attributes
451          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
452          if (contentTypeAttribute != null) {
453            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
454            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
455          }
456          
457          return content;
458        } catch (InvalidKeyException ex) {
459          throw new CMSException("Private key error: "+ex.toString());
460        } catch (NoSuchAlgorithmException ex) {
461          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
462        } catch (CodingException ex) {
463          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
464        }
465      }
466      
467      /**
468       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
469       * the recipient identified by its recipient certificate and verifies the message 
470       * authentication code.
471       *
472       * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
473       * @param key the key to decrypt the message
474       * @param recipientCert the certificate of the recipient
475       *
476       * @return the recovered message, as byte array
477       * @throws CMSException if the message cannot be recovered
478       * @throws IOException if a stream read/write error occurs
479       */
480      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert)
481        throws CMSException, IOException {
482    
483        // create the AuthEnvelopedData object from a BER encoded byte array
484        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
485        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
486    
487        System.out.println("Information about the encrypted data:");
488        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
489        System.out.println("Content type: "+eci.getContentType().getName());
490        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
491        
492        // decrypt the content encryption key and the content; verify mac
493        try {
494          System.out.println("Decrypt the content...");
495          authEnvelopedData.setupCipher(key, recipientCert);
496          InputStream decrypted = authEnvelopedData.getInputStream();
497          ByteArrayOutputStream os = new ByteArrayOutputStream();
498          Util.copyStream(decrypted, os, null);
499          byte[] content = os.toByteArray();
500    
501          // get authenticated attributes
502          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
503          if (contentTypeAttribute != null) {
504            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
505            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
506          }
507          
508          return content;
509        } catch (InvalidKeyException ex) {
510          throw new CMSException("Private key error: "+ex.toString());
511        } catch (NoSuchAlgorithmException ex) {
512          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
513        } catch (CodingException ex) {
514          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
515        }
516      }
517    
518    
519      // non stream
520    
521      /**
522       * Creates a CMS <code>AuthEnvelopedData</code> message.
523       * 
524       * @param message the message to be enveloped, as byte representation
525       * @param keyEA the key encryption (key agreement) algorithm used for creating 
526       *              a shared key encryption key for encrypting the secret content
527       *              encryption key with it
528       * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
529       *                   the content encryption key
530       * @param kekLength the length of the key encryption key to be created for
531       *                  encrypting the content encryption key with it    
532       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
533       *
534       * 
535       * @return the encoded <code>AuthEnvelopedData</code>, as byte array
536       * 
537       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
538       *                          be created
539       */
540      public byte[] createAuthEnvelopedData(byte[] message, AlgorithmID keyEA, AlgorithmID keyWrapAlg,
541        int kekLength, AlgorithmID contentAuthEncAlg)
542        throws CMSException {
543        
544        AuthEnvelopedData authEnvelopedData;
545    
546        // create a new AuthEnvelopedData object
547        authEnvelopedData = new AuthEnvelopedData(message, contentAuthEncAlg);
548        
549        //  create some authenticated attributes
550        try {
551          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
552          authEnvelopedData.setAuthenticatedAttributes(attributes);
553        } catch (Exception ex) {
554          throw new CMSException("Error creating attribute: " + ex.toString());   
555        }
556        
557        // set the RecipientInfos
558        RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
559        authEnvelopedData.setRecipientInfos(recipients);
560    
561        // wrap into ContentInfo
562        ContentInfo contentInfo = new ContentInfo(authEnvelopedData);
563        // return encoded EnvelopedData
564        return contentInfo.getEncoded();
565      }
566    
567    
568      /**
569       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
570       * the recipient identified by its index into the recipientInfos field and verifies
571       * the message authentication code.
572       * <p>
573       * This way of decrypting the content may be used for any type of RecipientInfo
574       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
575       * know at what index of the recipientInfo field the RecipientInfo for the 
576       * particular recipient in mind can be found. If the recipient in mind uses
577       * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
578       * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
579       * keys for more than only one recipient; since the recipientInfoIndex only
580       * specifies the RecipientInfo but not the encrypted content encryption key 
581       * -- if there are more than only one -- repeated decryption runs may be
582       * required as long as the decryption process completes successfully.
583       *
584       * @param enc the encoded <code>AuthEnvelopedData</code>
585       * 
586       * @param key the key to decrypt the message
587       * 
588       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
589       *                    to which the specified key belongs
590       *
591       * @return the recovered message, as byte array
592       * 
593       * @throws CMSException if the message cannot be recovered
594       * @throws IOException if an I/O error occurs
595       */
596      public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
597        throws CMSException, IOException {
598        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
599        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
600    
601        System.out.println("Information about the encrypted data:");
602        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
603        System.out.println("Content type: "+eci.getContentType().getName());
604        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
605    
606        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
607        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
608        
609        // for demonstration purposes we only look one time for all recipients included:
610        if (recipientInfoIndex == 0) {
611          int k = 0;
612          for (int i=0; i<recipients.length; i++) {
613            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
614            for (int j = 0; j < recipientIDs.length; j++) {
615              System.out.println("Recipient "+(++k)+":");
616              System.out.println(recipientIDs[j]);
617            }   
618          }
619        }
620        
621        // decrypt the message and verify the mac
622        try {
623          authEnvelopedData.setupCipher(key, recipientInfoIndex);
624          byte[] content = authEnvelopedData.getContent();
625    
626          // get authenticated attributes
627          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
628          if (contentTypeAttribute != null) {
629            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
630            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
631          }
632          
633          return content;
634        } catch (InvalidKeyException ex) {
635          throw new CMSException("Private key error: "+ex.toString());
636        } catch (NoSuchAlgorithmException ex) {
637          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
638        } catch (CodingException ex) {
639          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
640        }
641      }
642      
643      /**
644       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
645       * the recipient identified by recipient identifier.
646       * <p>
647       *
648       * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
649       * @param key the key to decrypt the message
650       * @param recipientID the recipient identifier uniquely identifying the key of the
651       *        recipient
652       *
653       * @return the recovered message, as byte array
654       * @throws CMSException if the message cannot be recovered
655       * @throws IOException if an I/O error occurs
656       */
657      public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
658        throws CMSException, IOException {
659        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
660        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
661    
662        System.out.println("Information about the encrypted data:");
663        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
664        System.out.println("Content type: "+eci.getContentType().getName());
665        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
666    
667        // get the right RecipientInfo
668        System.out.println("\nSearch for RecipientInfo:");
669        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
670        if (recipient != null) {
671          System.out.println("RecipientInfo: " + recipient);   
672        } else {
673          throw new CMSException("No recipient with ID: " + recipientID);
674        }    
675        // decrypt the content encryption key and the content
676        try {
677          System.out.println("Decrypt encrypted content encryption key...");
678          SecretKey cek = recipient.decryptKey(key, recipientID);
679          System.out.println("Decrypt content with decrypted content encryption key...");
680          // decrypt content and verify mac      
681          authEnvelopedData.setupCipher(cek);
682          byte[] content = authEnvelopedData.getContent();
683    
684          // get authenticated attributes
685          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
686          if (contentTypeAttribute != null) {
687            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
688            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
689          }
690          
691          return content;
692        } catch (InvalidKeyException ex) {
693          throw new CMSException("Private key error: "+ex.toString());
694        } catch (NoSuchAlgorithmException ex) {
695          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
696        } catch (CodingException ex) {
697          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
698        }
699      }
700      
701      /**
702       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
703       * the recipient identified by its recipient certificate or keyID.
704       *
705       * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
706       * @param key the key to decrypt the message
707       * @param recipientCert the certificate of the recipient 
708       *
709       * @return the recovered message, as byte array
710       * @throws CMSException if the message cannot be recovered
711       */
712      public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 
713        throws CMSException, IOException {
714        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
715        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
716    
717        System.out.println("Information about the encrypted data:");
718        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
719        System.out.println("Content type: "+eci.getContentType().getName());
720        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
721    
722        // decrypt the content encryption key and the content
723        try {
724          System.out.println("Decrypt the content and verify mac...");
725          // decrypt content and verify mac
726          authEnvelopedData.setupCipher(key, recipientCert);
727          
728          byte[] content = authEnvelopedData.getContent();
729    
730          // get authenticated attributes
731          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
732          if (contentTypeAttribute != null) {
733            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
734            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
735          }
736          
737          return content;
738        } catch (InvalidKeyException ex) {
739          throw new CMSException("Private key error: "+ex.toString());
740        } catch (NoSuchAlgorithmException ex) {
741          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
742        } catch (CodingException ex) {
743          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
744        }
745    
746      }
747      
748      /**
749       * Creates the RecipientInfos.
750       * 
751       * @param keyEA the key encryption (key agreement) algorithm used for creating 
752       *              a shared key encryption key for encrypting the secret content
753       *              encryption key with it
754       * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
755       *                   the content encryption key
756       * @param kekLength the length of the key encryption key to be created for
757       *                  encrypting the content encryption key with it              
758       *
759       * @return the RecipientInfos created, two KeyAgreeRecipientInfos
760       *
761       * @throws CMSException if an error occurs when creating the recipient infos
762       */
763      public RecipientInfo[] createRecipients(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException {
764        // just for demonstration we use two recipients, one having a x25519 key, the other a x448 key
765        RecipientInfo[] recipients = new RecipientInfo[2];
766        try {
767          recipients[0] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
768          ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(x25519User, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
769      
770          recipients[1] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
771          ((KeyAgreeRecipientInfo)recipients[1]).addRecipient(x448User, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
772          
773        } catch (Exception ex) {
774          throw new CMSException("Error adding recipients: " + ex.toString()); 
775        }    
776        return recipients;
777      }  
778      
779      /**
780       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
781       * using the index into the recipientInfos field for identifying the recipient.
782       *
783       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
784       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
785       *
786       * @throws Exception if some error occurs during decoding/decryption
787       */ 
788      public void parseAuthEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
789        byte[] receivedMessage;
790        if (stream) {
791          // x25519User
792          System.out.println("\nDecrypt for x25519User:");
793          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, 0);
794          System.out.print("\nDecrypted content: ");
795          System.out.println(new String(receivedMessage));
796          // x448User
797          System.out.println("\nDecrypt for x448User:");
798          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, 1);
799          System.out.print("\nDecrypted content: ");
800          System.out.println(new String(receivedMessage));
801    
802        } else {
803          // x25519User
804          System.out.println("\nDecrypt for x25519User:");
805          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, 0);
806          System.out.print("\nDecrypted content: ");
807          System.out.println(new String(receivedMessage));
808          // x448User
809          System.out.println("\nDecrypt for x448User:");
810          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, 1);
811          System.out.print("\nDecrypted content: ");
812          System.out.println(new String(receivedMessage));
813    
814        }    
815      }
816      
817      /**
818       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
819       * using their recipient identifiers for identifying the recipient.
820       *
821       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
822       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
823       *
824       * @throws Exception if some error occurs during decoding/decryption
825       */ 
826      public void parseAuthEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
827        byte[] receivedMessage;
828        if (stream) {
829          // x25519User
830          System.out.println("\nDecrypt for x25519User:");
831          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
832          System.out.print("\nDecrypted content: ");
833          System.out.println(new String(receivedMessage));
834          // x448User
835          System.out.println("\nDecrypt for x448User:");
836          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
837          System.out.print("\nDecrypted content: ");
838          System.out.println(new String(receivedMessage));
839        } else {
840          // x25519User
841          System.out.println("\nDecrypt for x25519User:");
842          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
843          System.out.print("\nDecrypted content: ");
844          System.out.println(new String(receivedMessage));
845          // x448User
846          System.out.println("\nDecrypt for x448User:");
847          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
848          System.out.print("\nDecrypted content: ");
849          System.out.println(new String(receivedMessage));
850        }    
851      }
852      
853      /**
854       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
855       * using their recipient certificate for identifying the recipient.
856       *
857       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
858       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
859       *
860       * @throws Exception if some error occurs during decoding/decryption
861       */ 
862      public void parseAuthEnvelopedDataWithRecipientCert(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
863        byte[] receivedMessage;
864        if (stream) {
865          // x25519User
866          System.out.println("\nDecrypt for x25519User:");
867          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, x25519User);
868          System.out.print("\nDecrypted content: ");
869          System.out.println(new String(receivedMessage));
870          // x448User
871          System.out.println("\nDecrypt for x448User:");
872          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, x448User);
873          System.out.print("\nDecrypted content: ");
874          System.out.println(new String(receivedMessage));
875        } else {
876          // x25519User
877          System.out.println("\nDecrypt for x25519User:");
878          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, x25519User);
879          System.out.print("\nDecrypted content: ");
880          System.out.println(new String(receivedMessage));
881          // x448User
882          System.out.println("\nDecrypt for x448User:");
883          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, x448User);
884          System.out.print("\nDecrypted content: ");
885          System.out.println(new String(receivedMessage));
886        }    
887      }
888      
889      /**
890       * Starts the test.
891       */
892      public void start() {
893      
894        AlgorithmID[] keyEAs = {
895            AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme, 
896            AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme, 
897            AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme, 
898            AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme,
899            AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme, 
900        };
901        
902        AlgorithmID[] keyWrapAlgs = {
903            CMSAlgorithmID.cms_aes128_wrap, 
904            CMSAlgorithmID.cms_aes192_wrap, 
905            CMSAlgorithmID.cms_aes256_wrap, 
906        };
907        
908        for (int i = 0; i < keyEAs.length; i++) {
909          AlgorithmID[] contentEAs = { AlgorithmID.aes256_GCM, AlgorithmID.aes256_CCM, AlgorithmID.chacha20Poly1305 };
910          AlgorithmID keyEA = keyEAs[i];
911          for (int j = 0; j < keyWrapAlgs.length; j++) {
912            AlgorithmID keyWrapAlg = keyWrapAlgs[j];
913            int kekLength = 256;
914            if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes192_wrap)) {
915              kekLength = 192;
916              contentEAs = new AlgorithmID[] { AlgorithmID.aes192_GCM, AlgorithmID.aes192_CCM };
917            } else if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes128_wrap)) {
918              kekLength = 128;
919              contentEAs = new AlgorithmID[] { AlgorithmID.aes128_GCM, AlgorithmID.aes128_CCM };
920            }
921            
922            for (int k = 0; k < contentEAs.length; k++) {
923              AlgorithmID contentAuthEncAlg = (AlgorithmID)contentEAs[k].clone();
924              System.out.println("EdDH AuthEnveloped demo for " + keyEA.getName() + " with " + keyWrapAlg.getName() +" and " + contentAuthEncAlg.getName());
925              start(keyEA, keyWrapAlg, kekLength, contentAuthEncAlg);
926            }
927          }
928        } 
929      }
930      
931      /**
932       * Starts the test for the given content-authenticated encryption algorithm.
933       * 
934       * @param keyEA the key encryption (key agreement) algorithm used for creating 
935       *              a shared key encryption key for encrypting the secret content
936       *              encryption key with it
937       * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
938       *                   the content encryption key
939       * @param kekLength the length of the key encryption key to be created for
940       *                  encrypting the content encryption key with it       
941       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
942       */
943      public void start(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength, AlgorithmID contentAuthEncAlg) {
944         // the test message
945        String m = "This is the test message.";
946        System.out.println("Test message: \""+m+"\"");
947        System.out.println();
948        byte[] message = m.getBytes();
949    
950        try {
951          byte[] encodedAuthEnvelopedData;
952          System.out.println("Stream implementation demos");
953          System.out.println("===========================");
954    
955          // the stream implementation
956          //
957          // test CMS AuthEnvelopedDataStream
958          //
959          System.out.println("\nCMS AuthEnvelopedDataStream demo [create]:\n");
960          encodedAuthEnvelopedData = createAuthEnvelopedDataStream(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
961          // transmit data
962          System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
963          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
964          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData);
965          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
966          parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData);
967          System.out.println("Decrypt for the several recipients using their certificate.");
968          parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData);
969          
970          
971          // the output stream implementation
972          //
973          // test CMS AuthEnvelopedDataOutputStream
974          //
975          System.out.println("\nCMS AuthEnvelopedDataOutputStream demo [create]:\n");
976          encodedAuthEnvelopedData = createAuthEnvelopedDataOutputStream(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
977          // transmit data
978          System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
979          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
980          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData);
981          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
982          parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData);
983          System.out.println("Decrypt for the several recipients using their certificate.");
984          parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData);
985    
986          // the non-stream implementation
987          System.out.println("\nNon-stream implementation demos");
988          System.out.println("===============================");
989    
990                
991          //
992          // test CMS AuthEnvelopedData
993          //
994          System.out.println("\nCMS AuthEnvelopedData demo [create]:\n");
995          encodedAuthEnvelopedData = createAuthEnvelopedData(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
996          // transmit data
997          System.out.println("\nCMS AuthEnvelopedData demo [parse]:\n");
998          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
999          parseAuthEnvelopedDataWithRecipientInfoIndex(false, encodedAuthEnvelopedData);
1000          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1001          parseAuthEnvelopedDataWithRecipientIdentifier(false, encodedAuthEnvelopedData);
1002          System.out.println("Decrypt for the several recipients using their certificate.");
1003          parseAuthEnvelopedDataWithRecipientCert(false, encodedAuthEnvelopedData);
1004          
1005    
1006            } catch (Exception ex) {
1007              ex.printStackTrace();
1008              throw new RuntimeException(ex.toString());
1009            }
1010      }
1011      
1012      
1013      /**
1014       * Main method.
1015       *
1016       * @throws IOException
1017       *            if an I/O error occurs when reading required keys
1018       *            and certificates from files
1019       */
1020      public static void main(String argv[]) throws Exception {
1021    
1022        DemoUtil.initDemos();
1023        ECCDemoUtil.installIaikEccProvider();
1024    
1025        (new EdDHAuthEnvelopedDataDemo()).start();
1026        System.out.println("\nReady!");
1027        DemoUtil.waitKey();
1028      }
1029      
1030    }