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