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