001    // Copyright (C) 2002 IAIK
002    // https://jce.iaik.tugraz.at
003    //
004    // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005    //                           Communication Technologies SIC
006    // https://sic.tech
007    //
008    // All rights reserved.
009    //
010    // Redistribution and use in source and binary forms, with or without
011    // modification, are permitted provided that the following conditions
012    // are met:
013    // 1. Redistributions of source code must retain the above copyright
014    //    notice, this list of conditions and the following disclaimer.
015    // 2. Redistributions in binary form must reproduce the above copyright
016    //    notice, this list of conditions and the following disclaimer in the
017    //    documentation and/or other materials provided with the distribution.
018    //
019    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029    // SUCH DAMAGE.
030    
031    // Copyright (C) 2002 IAIK
032    // https://sic.tech/
033    //
034    // Copyright (C) 2003 - 2025 Stiftung Secure Information and 
035    //                           Communication Technologies SIC
036    // https://sic.tech/
037    //
038    // All rights reserved.
039    //
040    // This source is provided for inspection purposes and recompilation only,
041    // unless specified differently in a contract with IAIK. This source has to
042    // be kept in strict confidence and must not be disclosed to any third party
043    // under any circumstances. Redistribution in source and binary forms, with
044    // or without modification, are <not> permitted in any case!
045    //
046    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056    // SUCH DAMAGE.
057    //
058    // $Header: /IAIK-CMS/current/src/demo/cms/ecc/ECDHAuthEnvelopedDataDemo.java 5     12.02.25 17:58 Dbratko $
059    // $Revision: 5 $
060    //
061    
062    
063    package demo.cms.ecc;
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    
073    import javax.crypto.SecretKey;
074    
075    import demo.DemoUtil;
076    import iaik.asn1.ObjectID;
077    import iaik.asn1.structures.AlgorithmID;
078    import iaik.asn1.structures.Attribute;
079    import iaik.cms.AuthEnvelopedData;
080    import iaik.cms.AuthEnvelopedDataOutputStream;
081    import iaik.cms.AuthEnvelopedDataStream;
082    import iaik.cms.CMSException;
083    import iaik.cms.ContentInfo;
084    import iaik.cms.ContentInfoOutputStream;
085    import iaik.cms.ContentInfoStream;
086    import iaik.cms.EncryptedContentInfo;
087    import iaik.cms.EncryptedContentInfoStream;
088    import iaik.cms.IssuerAndSerialNumber;
089    import iaik.cms.KeyIdentifier;
090    import iaik.cms.OriginatorInfo;
091    import iaik.cms.RecipientInfo;
092    import iaik.cms.RecipientKeyIdentifier;
093    import iaik.cms.attributes.CMSContentType;
094    import iaik.utils.Util;
095    import iaik.x509.X509Certificate;
096    
097    /**
098     * Demonstrates the usage of class {@link iaik.cms.AuthEnvelopedDataStream}, 
099     * {@link iaik.cms.AuthEnvelopedData} and {@link iaik.cms.AuthEnvelopedDataOutputStream}
100     * for authenticated encrypting data using the CMS type
101     * AuthEnvelopedData by using Static-Static ECDH according to <a href = 
102     * "http://www.ietf.org/rfc/rfc6278.txt" target="_blank">6278</a> as 
103     * key agreement method.
104     * <p>
105     * Any keys/certificates required for this demo are read from a keystore
106     * file "cmsecc.keystore" located in your current working directory. If
107     * the keystore file does not exist you can create it by running the
108     * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
109     * program. 
110     * <p>
111     * Additionally to <code>iaik_cms.jar</code> you also must have 
112     * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
113     * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
114     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
115     * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
116     * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
117     * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
118     * in your classpath.
119     *
120     * @see iaik.cms.AuthEnvelopedDataStream
121     * @see iaik.cms.AuthEnvelopedData
122     * @see iaik.cms.AuthEnvelopedDataOutputStream
123     * @see iaik.cms.RecipientInfo
124     * @see iaik.cms.KeyAgreeRecipientInfo
125     * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore
126     */
127    public class ECDHAuthEnvelopedDataDemo extends StaticStaticECDHDemo {
128    
129      
130    
131      /**
132       * Setup the demo certificate chains.
133       *
134       * Keys and certificates are retrieved from the demo keyStore file
135       * "cmsecc.keystore" located in your current working directory. If
136       * the keystore file does not exist you can create it by running the
137       * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
138       * program. 
139       *
140       * @throws IOException if keys/certificates cannot be read from the keystore
141       */
142      public ECDHAuthEnvelopedDataDemo() throws IOException {
143        super();
144        System.out.println();
145        System.out.println("**********************************************************************************");
146        System.out.println("*                          ECDH AuthEnvelopedData demo                           *");
147        System.out.println("*   (shows the usage of the CMS AuthEnvelopedData type implementation for ECDH)  *");
148        System.out.println("**********************************************************************************");
149        System.out.println();
150        
151        
152      }
153    
154      /**
155       * Creates a CMS <code>AuthEnvelopedDataStream</code> message.
156       *
157       * @param message the message to be authenticated-enveloped, as byte representation
158       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
159       * 
160       * @return the encoding of the <code>AuthEnvelopedData</code> object just created
161       * 
162       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
163       *                          be created
164       * @throws IOException if an I/O error occurs
165       */
166      public byte[] createAuthEnvelopedDataStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException {
167    
168        // we are testing the stream interface
169        ByteArrayInputStream is = new ByteArrayInputStream(message);
170    
171        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is, contentAuthEncAlg);
172        if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
173            contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
174            contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
175          // for aes-ccm we need to know the data input length in advance
176          authEnvelopedData.setInputLength(message.length);
177        }
178        
179        //  static-static mode: set OriginatorInfo
180        OriginatorInfo originator = new OriginatorInfo();
181        originator.setCertificates(ecdhOriginatorCerts_);
182        authEnvelopedData.setOriginatorInfo(originator);
183        
184        //  create some authenticated attributes
185        try {
186          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
187          authEnvelopedData.setAuthenticatedAttributes(attributes);
188        } catch (Exception ex) {
189          throw new CMSException("Error creating attribute: " + ex.toString());   
190        }
191       
192        // create the recipient infos
193        RecipientInfo[] recipients = createRecipients();
194        // specify the recipients of the encrypted message
195        authEnvelopedData.setRecipientInfos(recipients);
196    
197        // return the EnvelopedDate as DER encoded byte array with block size 4
198        // (just for testing; in real application we will use a proper blocksize,
199        //  e.g. 2048, 4096,..)
200        authEnvelopedData.setBlockSize(4);
201        ByteArrayOutputStream os = new ByteArrayOutputStream();
202        ContentInfoStream cis = new ContentInfoStream(authEnvelopedData);
203        cis.writeTo(os);
204        return os.toByteArray();
205      }
206      
207      /**
208       * Creates a CMS <code>AuthEnvelopedData</code> message using the
209       * {@link iaik.cms.AuthEnvelopedDataOutputStream AuthEnvelopedDataOutputStream}
210       * class.
211       *
212       * @param message the message to be authenticated-enveloped, as byte representation
213       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
214       * 
215       * @return the encoding of the <code>AuthEnvelopedData</code> object just created
216       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
217       *                          be created
218       * @throws IOException if an I/O error occurs
219       */
220      public byte[] createAuthEnvelopedDataOutputStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException {
221    
222        //  a stream from which to read the data to be encrypted
223        ByteArrayInputStream is = new ByteArrayInputStream(message);
224        
225        // the stream to which to write the AuthEnvelopedData
226        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
227    
228    
229        //  wrap AuthEnvelopedData into a ContentInfo 
230        ContentInfoOutputStream contentInfoStream = 
231          new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
232        // create a new AuthEnvelopedData object encrypted with AES
233    
234        AuthEnvelopedDataOutputStream  authEnvelopedData =
235            new AuthEnvelopedDataOutputStream(contentInfoStream, 
236                                              contentAuthEncAlg);
237      
238        if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
239            contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
240            contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
241          // for aes-ccm we need to know the data input length in advance
242          authEnvelopedData.setInputLength(message.length);
243        }
244        //  static-static mode: set OriginatorInfo
245        OriginatorInfo originator = new OriginatorInfo();
246        originator.setCertificates(ecdhOriginatorCerts_);
247        authEnvelopedData.setOriginatorInfo(originator);
248        
249        //  create some authenticated attributes
250        try {
251          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
252          authEnvelopedData.setAuthenticatedAttributes(attributes);
253        } catch (Exception ex) {
254          throw new CMSException("Error creating attribute: " + ex.toString());   
255        }
256        
257        // create the recipient infos
258        RecipientInfo[] recipients = createRecipients();
259        /// specify the recipients of the encrypted message
260        authEnvelopedData.setRecipientInfos(recipients);
261    
262        int blockSize = 4; // in real world we would use a block size like 2048
263        //  write in the data to be encrypted
264        byte[] buffer = new byte[blockSize];
265        int bytesRead;
266        while ((bytesRead = is.read(buffer)) != -1) {
267          authEnvelopedData.write(buffer, 0, bytesRead);
268        }
269        
270        // closing the stream finishes encryption and closes the underlying stream
271        authEnvelopedData.close();
272        return resultStream.toByteArray();
273      }
274    
275      /**
276       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
277       * the recipient identified by its index into the recipientInfos field.
278       *
279       * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
280       * @param key the key to decrypt the message
281       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
282       *                           to which the specified key belongs
283       *
284       * @return the recovered message, as byte array
285       * @throws CMSException if the message cannot be recovered
286       * @throws IOException if a stream read/write error occurs
287       */
288      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
289        throws CMSException, IOException {
290    
291        // create the AuthEnvelopedData object from a DER encoded byte array
292        // we are testing the stream interface
293        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
294        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
295    
296        System.out.println("Information about the encrypted data:");
297        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
298        System.out.println("Content type: "+eci.getContentType().getName());
299        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
300    
301        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
302        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
303        
304        // for demonstration purposes we only look one time for all recipients included:
305        if (recipientInfoIndex == 0) {
306          int k = 0;
307          for (int i=0; i<recipients.length; i++) {
308            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
309            for (int j = 0; j < recipientIDs.length; j++) {
310              System.out.println("Recipient "+(++k)+":");
311              System.out.println(recipientIDs[j]);
312            }   
313          }
314        }
315        // decrypt the message for the first recipient
316        try {
317          authEnvelopedData.setupCipher(key, recipientInfoIndex);
318          InputStream decrypted = authEnvelopedData.getInputStream();
319          ByteArrayOutputStream os = new ByteArrayOutputStream();
320          Util.copyStream(decrypted, os, null);
321    
322          return os.toByteArray();
323    
324        } catch (InvalidKeyException ex) {
325          throw new CMSException("Private key error: "+ex.getMessage());
326        } catch (NoSuchAlgorithmException ex) {
327          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
328        }
329      }
330      
331      /**
332       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
333       * the recipient identified by recipient identifier.
334       *
335       * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
336       * @param key the key to decrypt the message
337       * @param recipientID the recipient identifier uniquely identifying the key of the
338       *        recipient
339       *
340       * @return the recovered message, as byte array
341       * @throws CMSException if the message cannot be recovered
342       * @throws IOException if a stream read/write error occurs
343       */
344      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
345        throws CMSException, IOException {
346    
347        // create the AuthEnvelopedData object from a DER encoded byte array
348        // we are testing the stream interface
349        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
350        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
351    
352        System.out.println("Information about the encrypted data:");
353        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
354        System.out.println("Content type: "+eci.getContentType().getName());
355        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
356        
357        // get the right RecipientInfo
358        System.out.println("\nSearch for RecipientInfo:");
359        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
360        if (recipient != null) {
361          System.out.println("RecipientInfo: " + recipient);   
362        } else {
363          throw new CMSException("No recipient with ID: " + recipientID);
364        }    
365        // decrypt the content encryption key and the content
366        try {
367          System.out.println("Decrypt encrypted content encryption key...");
368          SecretKey cek = recipient.decryptKey(key, recipientID);
369          System.out.println("Decrypt content with decrypted content encryption key...");
370          authEnvelopedData.setupCipher(cek);
371          InputStream decrypted = authEnvelopedData.getInputStream();
372          ByteArrayOutputStream os = new ByteArrayOutputStream();
373          Util.copyStream(decrypted, os, null);
374    
375          return os.toByteArray();
376    
377        } catch (InvalidKeyException ex) {
378          throw new CMSException("Private key error: "+ex.getMessage());
379        } catch (NoSuchAlgorithmException ex) {
380          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
381        }
382      }
383      
384      // non stream
385    
386      /**
387       * Creates a CMS <code>AuthEnvelopedData</code> message.
388       * 
389       * @param message the message to be authenticated-enveloped, as byte representation
390       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
391       * 
392       * @return the encoded <code>AuthEnvelopedData</code>, as byte array
393       * 
394       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
395       *                          be created
396       */
397      public byte[] createAuthEnvelopedData(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException {
398        
399        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(message, contentAuthEncAlg);
400        
401        if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
402            contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
403            contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
404          // for aes-ccm we need to know the data input length in advance
405          authEnvelopedData.setInputLength(message.length);
406        }
407        
408        //  static-static mode: set OriginatorInfo
409        OriginatorInfo originator = new OriginatorInfo();
410        originator.setCertificates(ecdhOriginatorCerts_);
411        authEnvelopedData.setOriginatorInfo(originator);
412        
413        //  create some authenticated attributes
414        try {
415          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
416          authEnvelopedData.setAuthenticatedAttributes(attributes);
417        } catch (Exception ex) {
418          throw new CMSException("Error creating attribute: " + ex.toString());   
419        }
420         
421        // set the RecipientInfos
422        RecipientInfo[] recipients = createRecipients();
423        authEnvelopedData.setRecipientInfos(recipients);
424    
425        // return encoded AuthEnvelopedData
426        // wrap into contentInfo
427        ContentInfo ci = new ContentInfo(authEnvelopedData);
428        return ci.getEncoded();
429      }
430    
431    
432      /**
433       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
434       * the recipient identified by its index into the recipientInfos field.
435       *
436       * @param enc the encoded <code>AuthEnvelopedData</code>
437       * @param key the key to decrypt the message
438       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
439       *                           to which the specified key belongs
440       *
441       * @return the recovered message, as byte array
442       * 
443       * @throws CMSException if the message cannot be recovered
444       * @throws IOException if an I/O error occurs
445       */
446      public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
447        throws CMSException, IOException {
448        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
449        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
450    
451        System.out.println("Information about the encrypted data:");
452        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
453        System.out.println("Content type: "+eci.getContentType().getName());
454        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
455    
456        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
457        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
458        
459        // for demonstration purposes we only look one time for all recipients included:
460        if (recipientInfoIndex == 0) {
461          int k = 0;
462          for (int i=0; i<recipients.length; i++) {
463            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
464            for (int j = 0; j < recipientIDs.length; j++) {
465              System.out.println("Recipient "+(++k)+":");
466              System.out.println(recipientIDs[j]);
467            }   
468          }
469        }
470        
471        // decrypt the message
472        try {
473          authEnvelopedData.setupCipher(key, recipientInfoIndex);
474          return authEnvelopedData.getContent();
475    
476        } catch (InvalidKeyException ex) {
477          throw new CMSException("Private key error: "+ex.getMessage());
478        } catch (NoSuchAlgorithmException ex) {
479          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
480        }
481      }
482      
483      /**
484       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
485       * the recipient identified by recipient identifier.
486       * <p>
487       * This way of decrypting the content may be used for any type of RecipientInfo
488       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
489       * recipient in mind is identified by its recipient identifier.
490       *
491       * @param enc the encoded <code>AuthenticatedData</code>
492       * @param key the key to decrypt the message
493       * @param recipientID the recipient identifier uniquely identifying the key of the
494       *        recipient
495       *
496       * @return the recovered message, as byte array
497       * @throws CMSException if the message cannot be recovered
498       * @throws IOException if an I/O error occurs
499       */
500      public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
501        throws CMSException, IOException {
502        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
503        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
504    
505        System.out.println("Information about the encrypted data:");
506        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
507        System.out.println("Content type: "+eci.getContentType().getName());
508        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
509    
510        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
511    
512        // get the right RecipientInfo
513        System.out.println("\nSearch for RecipientInfo:");
514        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
515        if (recipient != null) {
516          System.out.println("RecipientInfo: " + recipient);   
517        } else {
518          throw new CMSException("No recipient with ID: " + recipientID);
519        }    
520        // decrypt the content encryption key and the content
521        try {
522          System.out.println("Decrypt encrypted content encryption key...");
523          SecretKey cek = recipient.decryptKey(key, recipientID);
524          System.out.println("Decrypt content with decrypted content encryption key...");
525          authEnvelopedData.setupCipher(cek);
526          return authEnvelopedData.getContent();
527    
528        } catch (InvalidKeyException ex) {
529          throw new CMSException("Private key error: "+ex.getMessage());
530        } catch (NoSuchAlgorithmException ex) {
531          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
532        }
533      }
534      
535      /**
536       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
537       * the recipient identified by its recipient certificate.
538       *
539       * @param enc the encoded <code>AuthEnvelopedData</code>
540       * @param key the key to decrypt the message
541       * @param recipientCert the certificate of the recipient 
542       *
543       * @return the recovered message, as byte array
544       *
545       * @throws CMSException if the message cannot be recovered
546       */
547      public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 
548        throws CMSException, IOException {
549        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
550        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
551    
552        System.out.println("Information about the encrypted data:");
553        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
554        System.out.println("Content type: "+eci.getContentType().getName());
555        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
556    
557        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
558    
559        // decrypt the content encryption key and the content
560        try {
561          System.out.println("Decrypt the content...");
562          authEnvelopedData.setupCipher(key, recipientCert);
563          return authEnvelopedData.getContent();
564    
565        } catch (InvalidKeyException ex) {
566          throw new CMSException("Private key error: "+ex.getMessage());
567        } catch (NoSuchAlgorithmException ex) {
568          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
569        }
570      }
571      
572      
573      /**
574       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
575       * using the index into the recipientInfos field for identifying the recipient.
576       *
577       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
578       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
579       *
580       * @throws Exception if some error occurs during decoding/decryption
581       */ 
582      public void parseAuthEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
583        byte[] receivedMessage;
584        if (stream) {
585          // ecdhUser1
586          System.out.println("\nDecrypt for ecdhUser1:");
587          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, 0);
588          System.out.print("\nDecrypted content: ");
589          System.out.println(new String(receivedMessage));
590          // ecdhUser2
591          System.out.println("\nDecrypt for ecdhUser2:");
592          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, 0);
593          System.out.print("\nDecrypted content: ");
594          System.out.println(new String(receivedMessage));
595        } else {
596          // ecdhUser1
597          System.out.println("\nDecrypt for ecdhUser1:");
598          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, 0);
599          System.out.print("\nDecrypted content: ");
600          System.out.println(new String(receivedMessage));
601          // ecdhUser2
602          System.out.println("\nDecrypt for ecdhUser2:");
603          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, 0);
604          System.out.print("\nDecrypted content: ");
605          System.out.println(new String(receivedMessage));
606        }    
607      }
608      
609      /**
610       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
611       * using their recipient identifiers for identifying the recipient.
612       *
613       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
614       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
615       *
616       * @throws Exception if some error occurs during decoding/decryption
617       */ 
618      public void parseAuthEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
619        byte[] receivedMessage;
620        if (stream) {
621          // ecdhUser1
622          System.out.println("\nDecrypt for ecdhUser1:");
623          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
624          System.out.print("\nDecrypted content: ");
625          System.out.println(new String(receivedMessage));
626          // ecdhUser2
627          System.out.println("\nDecrypt for ecdhUser2:");
628          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
629          System.out.print("\nDecrypted content: ");
630          System.out.println(new String(receivedMessage));
631        } else {
632          // ecdhUser1
633          System.out.println("\nDecrypt for ecdhUser1:");
634          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
635          System.out.print("\nDecrypted content: ");
636          System.out.println(new String(receivedMessage));
637          // ecdhUser2
638          System.out.println("\nDecrypt for ecdhUser2:");
639          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
640          System.out.print("\nDecrypted content: ");
641          System.out.println(new String(receivedMessage));
642        }    
643      }
644      
645      
646      
647      /**
648       * Starts the demo.
649       */
650      public void start() {
651       
652        // AES-GCM
653        AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone();
654        start(contentAuthEncAlg);
655        
656        // AES-CCM
657        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_CCM.clone();
658        start(contentAuthEncAlg);
659        
660        
661        // ChaCha20Poly1305
662        contentAuthEncAlg = (AlgorithmID)AlgorithmID.chacha20Poly1305.clone();
663        start(contentAuthEncAlg);
664      }
665      
666      /**
667       * Starts the demo for the given content-authenticated encryption algorithm.
668       * 
669       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
670       */
671      public void start(AlgorithmID contentAuthEncAlg) {
672         // the test message
673        String m = "This is the test message.";
674        System.out.println("Test message: \""+m+"\"");
675        System.out.println();
676        byte[] message = m.getBytes();
677    
678        try {
679          byte[] encoding;
680          System.out.println("Stream implementation demos");
681          System.out.println("===========================");
682    
683    
684          // the stream implementation
685          //
686          // test CMS AuthEnvelopedDataStream
687          //
688          System.out.println("\nCMS AuthEnvelopedDataStream demo [create]:\n");
689          encoding = createAuthEnvelopedDataStream(message, (AlgorithmID)contentAuthEncAlg.clone());
690          // transmit data
691          System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
692          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
693          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding);
694          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
695          parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding);
696          
697          
698          System.out.println("\nOutputStream implementation demos");
699          System.out.println("=================================");
700    
701    
702          // the output stream implementation
703          //
704          // test CMS AuthEnvelopedDataOutputStream
705          //
706          System.out.println("\nCMS AuthEnvelopedDataOutputStream demo [create]:\n");
707          encoding = createAuthEnvelopedDataOutputStream(message, (AlgorithmID)contentAuthEncAlg.clone());
708          // transmit data
709          System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
710          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
711          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding);
712          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
713          parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding);
714          
715          // the non-stream implementation
716          System.out.println("\nNon-stream implementation demos");
717          System.out.println("===============================");
718    
719                
720          //
721          // test CMS AuthEnvelopedData
722          //
723          System.out.println("\nCMS AuthEnvelopedData demo [create]:\n");
724          encoding = createAuthEnvelopedData(message, (AlgorithmID)contentAuthEncAlg.clone());
725          // transmit data
726          System.out.println("\nCMS AuthEnvelopedData demo [parse]:\n");
727          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
728          parseAuthEnvelopedDataWithRecipientInfoIndex(false, encoding);
729          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
730          parseAuthEnvelopedDataWithRecipientIdentifier(false, encoding);
731          
732          
733    
734        } catch (Exception ex) {
735          ex.printStackTrace();
736          throw new RuntimeException(ex.toString());
737        }
738      }
739      
740      /**
741       * Main method.
742       *
743       * @throws IOException
744       *            if an I/O error occurs when reading required keys
745       *            and certificates from the keystore file
746       */
747      public static void main(String argv[]) throws Exception {
748    
749        DemoUtil.initDemos();
750        ECCDemoUtil.installIaikEccProvider();
751        (new ECDHAuthEnvelopedDataDemo()).start();
752        System.out.println("\nReady!");
753        System.in.read();
754      }
755    }