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