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