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