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/ECDHEnvelopedDataDemo.java 27    12.02.25 17:58 Dbratko $
059    // $Revision: 27 $
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    import java.security.PrivateKey;
073    import java.security.SecureRandom;
074    
075    import javax.crypto.SecretKey;
076    
077    import demo.DemoUtil;
078    import demo.cms.ecc.keystore.CMSEccKeyStore;
079    import iaik.asn1.ObjectID;
080    import iaik.asn1.structures.AlgorithmID;
081    import iaik.cms.CMSAlgorithmID;
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.KeyAgreeRecipientInfo;
094    import iaik.cms.KeyIdentifier;
095    import iaik.cms.RecipientInfo;
096    import iaik.cms.RecipientKeyIdentifier;
097    import iaik.security.random.SecRandom;
098    import iaik.utils.Util;
099    import iaik.x509.X509Certificate;
100    
101    /**
102     * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataStream} and
103     * {@link iaik.cms.EnvelopedData} for encrypting data using the CMS type
104     * EnvelopedData by using Ephemeral-Static ECDH according to <a href = 
105     * "http://www.ietf.org/rfc/rfc3278.txt" target="_blank">3278</a> as 
106     * key agreement method.
107     * <p>
108     * Any keys/certificates required for this demo are read from a keystore
109     * file "cmsecc.keystore" located in your current working directory. If
110     * the keystore file does not exist you can create it by running the
111     * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
112     * program. 
113     * <p>
114     * Additionally to <code>iaik_cms.jar</code> you also must have 
115     * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
116     * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
117     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
118     * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
119     * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
120     * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
121     * in your classpath.
122     *
123     * @see iaik.cms.EnvelopedDataStream
124     * @see iaik.cms.EnvelopedData
125     * @see iaik.cms.RecipientInfo
126     * @see iaik.cms.KeyAgreeRecipientInfo
127     * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore
128     */
129    public class ECDHEnvelopedDataDemo {
130    
131      // certificate of ecdhUser 1
132      X509Certificate ecdhUser1;
133      // private key of ecdhUser 1
134      PrivateKey ecdhUser1_pk;
135      // certificate of ecdhUser 2
136      X509Certificate ecdhUser2;
137      // private key of ecdhUser 2
138      PrivateKey ecdhUser2_pk;
139      
140      // secure random number generator
141      SecureRandom random;
142    
143      /**
144       * Setup the demo certificate chains.
145       *
146       * Keys and certificates are retrieved from the demo keyStore file
147       * "cmsecc.keystore" located in your current working directory. If
148       * the keystore file does not exist you can create it by running the
149       * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
150       * program. 
151       *
152       * @throws IOException if keys/certificates cannot be read from the keystore
153       */
154      public ECDHEnvelopedDataDemo() throws IOException {
155        
156        System.out.println();
157        System.out.println("**********************************************************************************");
158        System.out.println("*                           ECDHEnvelopedData demo                               *");
159        System.out.println("*   (shows the usage of the CMS EnvelopedData type implementation for ECDH)      *");
160        System.out.println("**********************************************************************************");
161        System.out.println();
162        
163        // add all certificates to the list
164        
165        ecdhUser1 = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_192_CRYPT_1)[0];
166        ecdhUser1_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_192_CRYPT_1);
167        ecdhUser2 = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_192_CRYPT_2)[0];
168        ecdhUser2_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_192_CRYPT_2);
169        
170        random = SecRandom.getDefault();
171        
172      }
173    
174      /**
175       * Creates a CMS <code>EnvelopedDataStream</code> message.
176       *
177       * @param message the message to be enveloped, as byte representation
178       * @return the DER encoding of the <code>EnvelopedData</code> object just created
179       * @throws CMSException if the <code>EnvelopedData</code> object cannot
180       *                          be created
181       * @throws IOException if an I/O error occurs
182       */
183      public byte[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException {
184    
185        EnvelopedDataStream enveloped_data;
186    
187        // we are testing the stream interface
188        ByteArrayInputStream is = new ByteArrayInputStream(message);
189        // create a new EnvelopedData object encrypted with AES
190        try {
191          enveloped_data = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
192        } catch (NoSuchAlgorithmException ex) {
193          throw new CMSException(ex.toString());
194        }
195    
196    
197        // create the recipient infos
198        RecipientInfo[] recipients = createRecipients();
199        // specify the recipients of the encrypted message
200        enveloped_data.setRecipientInfos(recipients);
201    
202        // return the EnvelopedDate as DER encoded byte array with block size 4
203        // (just for testing; in real application we will use a proper blocksize,
204        //  e.g. 2048, 4096,..)
205        enveloped_data.setBlockSize(4);
206        ByteArrayOutputStream os = new ByteArrayOutputStream();
207        ContentInfoStream cis = new ContentInfoStream(enveloped_data);
208        cis.writeTo(os);
209        return os.toByteArray();
210      }
211      
212      /**
213       * Creates a CMS <code>EnvelopedData</code> message using the
214       * {@link iaik.cms.EnvelopedDataOutputStream EnvelopedDataOutputStream}
215       * class.
216       *
217       * @param message the message to be enveloped, as byte representation
218       * @return the DER encoding of the <code>EnvelopedData</code> object just created
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[] createEnvelopedDataOutputStream(byte[] message) throws CMSException, IOException {
224    
225        EnvelopedDataStream enveloped_data;
226    
227        //  a stream from which to read the data to be encrypted
228        ByteArrayInputStream is = new ByteArrayInputStream(message);
229        
230        // the stream to which to write the EnvelopedData
231        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
232        EnvelopedDataOutputStream envelopedData;
233    
234        //  wrap EnvelopedData into a ContentInfo 
235        ContentInfoOutputStream contentInfoStream = 
236          new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
237        // create a new EnvelopedData object encrypted with AES
238        try {
239          envelopedData = new EnvelopedDataOutputStream(contentInfoStream, 
240                                                        (AlgorithmID)AlgorithmID.aes256_CBC.clone());
241        } catch (NoSuchAlgorithmException ex) {
242          throw new CMSException(ex.toString());
243        }
244    
245    
246        // create the recipient infos
247        RecipientInfo[] recipients = createRecipients();
248        /// specify the recipients of the encrypted message
249        envelopedData.setRecipientInfos(recipients);
250    
251        int blockSize = 4; // in real world we would use a block size like 2048
252        //  write in the data to be encrypted
253        byte[] buffer = new byte[blockSize];
254        int bytesRead;
255        while ((bytesRead = is.read(buffer)) != -1) {
256          envelopedData.write(buffer, 0, bytesRead);
257        }
258        
259        // closing the stream finishes encryption and closes the underlying stream
260        envelopedData.close();
261        return resultStream.toByteArray();
262      }
263    
264      /**
265       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
266       * the recipient identified by its index into the recipientInfos field.
267       *
268       * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
269       * @param key the key to decrypt the message
270       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
271       *                           to which the specified key belongs
272       *
273       * @return the recovered message, as byte array
274       * @throws CMSException if the message cannot be recovered
275       * @throws IOException if a stream read/write error occurs
276       */
277      public byte[] getEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
278        throws CMSException, IOException {
279    
280        // create the EnvelopedData object from a DER encoded byte array
281        // we are testing the stream interface
282        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
283        EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
284    
285        System.out.println("Information about the encrypted data:");
286        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
287        System.out.println("Content type: "+eci.getContentType().getName());
288        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
289    
290        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
291        RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
292        
293        // for demonstration purposes we only look one time for all recipients included:
294        if (recipientInfoIndex == 0) {
295          int k = 0;
296          for (int i=0; i<recipients.length; i++) {
297            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
298            for (int j = 0; j < recipientIDs.length; j++) {
299              System.out.println("Recipient "+(++k)+":");
300              System.out.println(recipientIDs[j]);
301            }   
302          }
303        }
304        // decrypt the message for the first recipient
305        try {
306          enveloped_data.setupCipher(key, recipientInfoIndex);
307          InputStream decrypted = enveloped_data.getInputStream();
308          ByteArrayOutputStream os = new ByteArrayOutputStream();
309          Util.copyStream(decrypted, os, null);
310    
311          return os.toByteArray();
312    
313        } catch (InvalidKeyException ex) {
314          throw new CMSException("Private key error: "+ex.getMessage());
315        } catch (NoSuchAlgorithmException ex) {
316          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
317        }
318      }
319      
320      /**
321       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
322       * the recipient identified by recipient identifier.
323       *
324       * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
325       * @param key the key to decrypt the message
326       * @param recipientID the recipient identifier uniquely identifying the key of the
327       *        recipient
328       *
329       * @return the recovered message, as byte array
330       * @throws CMSException if the message cannot be recovered
331       * @throws IOException if a stream read/write error occurs
332       */
333      public byte[] getEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
334        throws CMSException, IOException {
335    
336        // create the EnvelopedData object from a DER encoded byte array
337        // we are testing the stream interface
338        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
339        EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
340    
341        System.out.println("Information about the encrypted data:");
342        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
343        System.out.println("Content type: "+eci.getContentType().getName());
344        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
345        
346        // get the right RecipientInfo
347        System.out.println("\nSearch for RecipientInfo:");
348        RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
349        if (recipient != null) {
350          System.out.println("RecipientInfo: " + recipient);   
351        } else {
352          throw new CMSException("No recipient with ID: " + recipientID);
353        }    
354        // decrypt the content encryption key and the content
355        try {
356          System.out.println("Decrypt encrypted content encryption key...");
357          SecretKey cek = recipient.decryptKey(key, recipientID);
358          System.out.println("Decrypt content with decrypted content encryption key...");
359          enveloped_data.setupCipher(cek);
360          InputStream decrypted = enveloped_data.getInputStream();
361          ByteArrayOutputStream os = new ByteArrayOutputStream();
362          Util.copyStream(decrypted, os, null);
363    
364          return os.toByteArray();
365    
366        } catch (InvalidKeyException ex) {
367          throw new CMSException("Private key error: "+ex.getMessage());
368        } catch (NoSuchAlgorithmException ex) {
369          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
370        }
371      }
372      
373      // non stream
374    
375      /**
376       * Creates a CMS <code>EnvelopedData</code> message.
377       * 
378       * @param message the message to be enveloped, as byte representation
379       * 
380       * @return the encoded <code>EnvelopedData</code>, as byte array
381       * 
382       * @throws CMSException if the <code>EnvelopedData</code> object cannot
383       *                          be created
384       */
385      public byte[] createEnvelopedData(byte[] message) throws CMSException {
386        
387        EnvelopedData enveloped_data;
388    
389        // create a new EnvelopedData object encrypted with AES
390        try {
391          enveloped_data = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
392        } catch (NoSuchAlgorithmException ex) {
393          throw new CMSException(ex.toString());
394        }
395        
396        // set the RecipientInfos
397        RecipientInfo[] recipients = createRecipients();
398        enveloped_data.setRecipientInfos(recipients);
399    
400        // return encoded EnvelopedData
401        // wrap into contentInfo
402        ContentInfo ci = new ContentInfo(enveloped_data);
403        return ci.getEncoded();
404      }
405    
406    
407      /**
408       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
409       * the recipient identified by its index into the recipientInfos field.
410       *
411       * @param enc the encoded <code>EnvelopedData</code>
412       * @param key the key to decrypt the message
413       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
414       *                           to which the specified key belongs
415       *
416       * @return the recovered message, as byte array
417       * 
418       * @throws CMSException if the message cannot be recovered
419       * @throws IOException if an I/O error occurs
420       */
421      public byte[] getEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
422        throws CMSException, IOException {
423        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
424        EnvelopedData enveloped_data = new EnvelopedData(bais);
425    
426        System.out.println("Information about the encrypted data:");
427        EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
428        System.out.println("Content type: "+eci.getContentType().getName());
429        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
430    
431        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
432        RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
433        
434        // for demonstration purposes we only look one time for all recipients included:
435        if (recipientInfoIndex == 0) {
436          int k = 0;
437          for (int i=0; i<recipients.length; i++) {
438            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
439            for (int j = 0; j < recipientIDs.length; j++) {
440              System.out.println("Recipient "+(++k)+":");
441              System.out.println(recipientIDs[j]);
442            }   
443          }
444        }
445        
446        // decrypt the message
447        try {
448          enveloped_data.setupCipher(key, recipientInfoIndex);
449          return enveloped_data.getContent();
450    
451        } catch (InvalidKeyException ex) {
452          throw new CMSException("Private key error: "+ex.getMessage());
453        } catch (NoSuchAlgorithmException ex) {
454          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
455        }
456      }
457      
458      /**
459       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
460       * the recipient identified by recipient identifier.
461       * <p>
462       * This way of decrypting the content may be used for any type of RecipientInfo
463       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
464       * recipient in mind is identified by its recipient identifier.
465       *
466       * @param enc the encoded <code>AuthenticatedData</code>
467       * @param key the key to decrypt the message
468       * @param recipientID the recipient identifier uniquely identifying the key of the
469       *        recipient
470       *
471       * @return the recovered message, as byte array
472       * @throws CMSException if the message cannot be recovered
473       * @throws IOException if an I/O error occurs
474       */
475      public byte[] getEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
476        throws CMSException, IOException {
477        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
478        EnvelopedData enveloped_data = new EnvelopedData(bais);
479    
480        System.out.println("Information about the encrypted data:");
481        EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
482        System.out.println("Content type: "+eci.getContentType().getName());
483        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
484    
485        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
486    
487        // get the right RecipientInfo
488        System.out.println("\nSearch for RecipientInfo:");
489        RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
490        if (recipient != null) {
491          System.out.println("RecipientInfo: " + recipient);   
492        } else {
493          throw new CMSException("No recipient with ID: " + recipientID);
494        }    
495        // decrypt the content encryption key and the content
496        try {
497          System.out.println("Decrypt encrypted content encryption key...");
498          SecretKey cek = recipient.decryptKey(key, recipientID);
499          System.out.println("Decrypt content with decrypted content encryption key...");
500          enveloped_data.setupCipher(cek);
501          return enveloped_data.getContent();
502    
503        } catch (InvalidKeyException ex) {
504          throw new CMSException("Private key error: "+ex.getMessage());
505        } catch (NoSuchAlgorithmException ex) {
506          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
507        }
508      }
509      
510      /**
511       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
512       * the recipient identified by its recipient certificate.
513       *
514       * @param enc the encoded <code>EnvelopedData</code>
515       * @param key the key to decrypt the message
516       * @param recipientCert the certificate of the recipient 
517       *
518       * @return the recovered message, as byte array
519       *
520       * @throws CMSException if the message cannot be recovered
521       */
522      public byte[] getEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 
523        throws CMSException, IOException {
524        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
525        EnvelopedData enveloped_data = new EnvelopedData(bais);
526    
527        System.out.println("Information about the encrypted data:");
528        EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
529        System.out.println("Content type: "+eci.getContentType().getName());
530        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
531    
532        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
533    
534        // decrypt the content encryption key and the content
535        try {
536          System.out.println("Decrypt the content...");
537          enveloped_data.setupCipher(key, recipientCert);
538          return enveloped_data.getContent();
539    
540        } catch (InvalidKeyException ex) {
541          throw new CMSException("Private key error: "+ex.getMessage());
542        } catch (NoSuchAlgorithmException ex) {
543          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
544        }
545      }
546      
547      /**
548       * Creates the RecipientInfos.
549       *
550       * @return the RecipientInfos created, two KeyAgreeRecipientInfos
551       *
552       * @throws CMSException if an error occurs when creating the recipient infos
553       */
554      public RecipientInfo[] createRecipients() throws CMSException {
555        
556        RecipientInfo[] recipients = new RecipientInfo[2];
557        try {
558          // recipients use key agreement
559          // the key encryption (key agreement) algorithm to use:
560          AlgorithmID keyEA1 = (AlgorithmID)CMSAlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme.clone();
561          // the key wrap algorithm to use:
562          AlgorithmID keyWrapAlg1 = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
563          // the length of the key encryption key to be generated:
564          int kekLength1 = 256;
565          recipients[0] = new KeyAgreeRecipientInfo(keyEA1, keyWrapAlg1, kekLength1);
566          // ecdhUser1 is the first receiver  (cert identified by IssuerAndSerialNumber)
567          ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(ecdhUser1, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
568      
569          // ecdhUser2 is the second receiver (cert identified by RecipientKeyIdentifier)
570          // the key encryption (key agreement) algorithm to use (just for demonstration purposes we use a second KeyAgreeRecipeintInfo):
571          AlgorithmID keyEA2 = (AlgorithmID)CMSAlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme.clone();
572          // the key wrap algorithm to use:
573          AlgorithmID keyWrapAlg2 = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
574          // the length of the key encryption key to be generated:
575          int kekLength2 = 256;
576          recipients[1] = new KeyAgreeRecipientInfo(keyEA2, keyWrapAlg2, kekLength2);
577          // ecdhUser1 is the first receiver  (cert identified by RecipientKeyIdentifier)
578          ((KeyAgreeRecipientInfo)recipients[1]).addRecipient(ecdhUser2, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
579          
580        } catch (Exception ex) {
581          throw new CMSException("Error adding recipients: " + ex.toString()); 
582        }    
583        return recipients;
584      }  
585      
586      /**
587       * Parses an EnvelopedData and decrypts the content for all test recipients
588       * using the index into the recipientInfos field for identifying the recipient.
589       *
590       * @param stream whether to use EnvelopedDataStream or EnvelopedData
591       * @param encodedEnvelopedData the encoded EnvelopedData object 
592       *
593       * @throws Exception if some error occurs during decoding/decryption
594       */ 
595      public void parseEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedEnvelopedData) throws Exception {
596        byte[] receivedMessage;
597        if (stream) {
598          // ecdhUser1
599          System.out.println("\nDecrypt for ecdhUser1:");
600          receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, ecdhUser1_pk, 0);
601          System.out.print("\nDecrypted content: ");
602          System.out.println(new String(receivedMessage));
603          // ecdhUser2
604          System.out.println("\nDecrypt for ecdhUser2:");
605          receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, ecdhUser2_pk, 1);
606          System.out.print("\nDecrypted content: ");
607          System.out.println(new String(receivedMessage));
608        } else {
609          // ecdhUser1
610          System.out.println("\nDecrypt for ecdhUser1:");
611          receivedMessage = getEnvelopedData(encodedEnvelopedData, ecdhUser1_pk, 0);
612          System.out.print("\nDecrypted content: ");
613          System.out.println(new String(receivedMessage));
614          // ecdhUser2
615          System.out.println("\nDecrypt for ecdhUser2:");
616          receivedMessage = getEnvelopedData(encodedEnvelopedData, ecdhUser2_pk, 1);
617          System.out.print("\nDecrypted content: ");
618          System.out.println(new String(receivedMessage));
619        }    
620      }
621      
622      /**
623       * Parses an EnvelopedData and decrypts the content for all test recipients
624       * using their recipient identifiers for identifying the recipient.
625       *
626       * @param stream whether to use EnvelopedDataStream or EnvelopedData
627       * @param encodedEnvelopedData the encoded EnvelopedData object 
628       *
629       * @throws Exception if some error occurs during decoding/decryption
630       */ 
631      public void parseEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedEnvelopedData) throws Exception {
632        byte[] receivedMessage;
633        if (stream) {
634          // ecdhUser1
635          System.out.println("\nDecrypt for ecdhUser1:");
636          receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, ecdhUser1_pk, new IssuerAndSerialNumber(ecdhUser1));
637          System.out.print("\nDecrypted content: ");
638          System.out.println(new String(receivedMessage));
639          // ecdhUser2
640          System.out.println("\nDecrypt for ecdhUser2:");
641          receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, ecdhUser2_pk, new RecipientKeyIdentifier(ecdhUser2));
642          System.out.print("\nDecrypted content: ");
643          System.out.println(new String(receivedMessage));
644        } else {
645          // ecdhUser1
646          System.out.println("\nDecrypt for ecdhUser1:");
647          receivedMessage = getEnvelopedData(encodedEnvelopedData, ecdhUser1_pk, new IssuerAndSerialNumber(ecdhUser1));
648          System.out.print("\nDecrypted content: ");
649          System.out.println(new String(receivedMessage));
650          // ecdhUser2
651          System.out.println("\nDecrypt for ecdhUser2:");
652          receivedMessage = getEnvelopedData(encodedEnvelopedData, ecdhUser2_pk, new RecipientKeyIdentifier(ecdhUser2));
653          System.out.print("\nDecrypted content: ");
654          System.out.println(new String(receivedMessage));
655        }    
656      }
657      
658      
659      
660      /**
661       * Starts the test.
662       */
663      public void start() {
664         // the test message
665        String m = "This is the test message.";
666        System.out.println("Test message: \""+m+"\"");
667        System.out.println();
668        byte[] message = m.getBytes();
669    
670        try {
671          byte[] encoding;
672          System.out.println("Stream implementation demos");
673          System.out.println("===========================");
674    
675    
676          // the stream implementation
677          //
678          // test CMS EnvelopedDataStream
679          //
680          System.out.println("\nCMS EnvelopedDataStream demo [create]:\n");
681          encoding = createEnvelopedDataStream(message);
682          // transmit data
683          System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n");
684          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
685          parseEnvelopedDataWithRecipientInfoIndex(true, encoding);
686          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
687          parseEnvelopedDataWithRecipientIdentifier(true, encoding);
688          
689          
690          System.out.println("\nOutputStream implementation demos");
691          System.out.println("=================================");
692    
693    
694          // the output stream implementation
695          //
696          // test CMS EnvelopedDataOutputStream
697          //
698          System.out.println("\nCMS EnvelopedDataOutputStream demo [create]:\n");
699          encoding = createEnvelopedDataOutputStream(message);
700          // transmit data
701          System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n");
702          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
703          parseEnvelopedDataWithRecipientInfoIndex(true, encoding);
704          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
705          parseEnvelopedDataWithRecipientIdentifier(true, encoding);
706          
707          // the non-stream implementation
708          System.out.println("\nNon-stream implementation demos");
709          System.out.println("===============================");
710    
711                
712          //
713          // test CMS EnvelopedData
714          //
715          System.out.println("\nCMS EnvelopedData demo [create]:\n");
716          encoding = createEnvelopedData(message);
717          // transmit data
718          System.out.println("\nCMS EnvelopedData demo [parse]:\n");
719          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
720          parseEnvelopedDataWithRecipientInfoIndex(false, encoding);
721          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
722          parseEnvelopedDataWithRecipientIdentifier(false, encoding);
723          
724          
725    
726        } catch (Exception ex) {
727          ex.printStackTrace();
728          throw new RuntimeException(ex.toString());
729        }
730      }
731      
732      /**
733       * Main method.
734       *
735       * @throws IOException
736       *            if an I/O error occurs when reading required keys
737       *            and certificates from the keystore file
738       */
739      public static void main(String argv[]) throws Exception {
740    
741        DemoUtil.initDemos();
742        ECCDemoUtil.installIaikEccProvider();
743        (new ECDHEnvelopedDataDemo()).start();
744        System.out.println("\nReady!");
745        System.in.read();
746      }
747    }