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