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/EdDHAuthenticatedDataDemo.java 6     12.02.25 17:58 Dbratko $
029// $Revision: 6 $
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.asn1.structures.Attribute;
052import iaik.cms.AuthenticatedData;
053import iaik.cms.AuthenticatedDataOutputStream;
054import iaik.cms.AuthenticatedDataStream;
055import iaik.cms.CMSAlgorithmID;
056import iaik.cms.CMSException;
057import iaik.cms.CertificateIdentifier;
058import iaik.cms.ContentInfo;
059import iaik.cms.ContentInfoOutputStream;
060import iaik.cms.ContentInfoStream;
061import iaik.cms.IssuerAndSerialNumber;
062import iaik.cms.KeyAgreeRecipientInfo;
063import iaik.cms.KeyIdentifier;
064import iaik.cms.RecipientInfo;
065import iaik.cms.RecipientKeyIdentifier;
066import iaik.cms.attributes.CMSContentType;
067import iaik.security.random.SecRandom;
068import iaik.utils.Util;
069import iaik.x509.X509Certificate;
070
071
072/**
073 * Demonstrates the usage of class {@link iaik.cms.AuthenticatedDataStream},
074 * {@link iaik.cms.AuthenticatedData} and {@link iaik.cms.AuthenticatedDataOutputStream} 
075 * for authenticated data with the CMS content type AuthenticatedData using the 
076 * Elliptic Curve Diffie-Hellman (ECDH) key agreement algorithm with curve25519 and
077 * curve448 according to <a href = "http://www.ietf.org/rfc/rfc5652.txt" target="_blank">RFC 5652</a>
078 * and <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.AuthenticatedDataStream
096 * @see iaik.cms.AuthenticatedData
097 * @see iaik.cms.AuthenticatedDataOutputStream
098 * @see iaik.cms.RecipientInfo
099 * @see iaik.cms.KeyAgreeRecipientInfo
100 */
101public class EdDHAuthenticatedDataDemo {
102
103  //certificate of x25519 user
104  X509Certificate x25519User_;
105  // private key of x25519 user
106  PrivateKey x25519UserPk_;
107  // certificate of x448 user 
108  X509Certificate x448User_;
109  // private key of x448 user 
110  PrivateKey x448UserPk_;
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 ("cms.keystore")
119   * file which has to be located in your current working directory and may be
120   * created by running {@link demo.keystore.SetupCMSKeyStore
121   * SetupCMSKeyStore}.
122   *
123   * @throws IOException if an file read error occurs
124   */
125  public EdDHAuthenticatedDataDemo() throws IOException {
126    
127    System.out.println();
128    System.out.println("***************************************************************************************************");
129    System.out.println("*                                    EdDHAuthenticatedDataDemo                                    *");
130    System.out.println("* (shows the usage of the CMS AuthenticatedData type implementation with curve25519 and curve448) *");
131    System.out.println("***************************************************************************************************");
132    System.out.println();
133    
134    // get keys and certificate from the demo keystore
135    x25519User_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519)[0];
136    x25519UserPk_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519);
137    x448User_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448)[0];
138    x448UserPk_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448);
139    random = SecRandom.getDefault();
140    
141  }
142
143
144  /**
145   * Creates a CMS <code>AuthenticatedDataStream</code> for the given message message.
146   *
147   * @param message the message to be authenticated, as byte representation
148   * @param macAlgorithm the mac algorithm to be used
149   * @param macKeyLength the length of the temporary MAC key to be generated 
150   * @param digestAlgorithm the digest algorithm to be used to calculate a digest
151   *                        from the content if authenticated attributes should
152   *                        be included 
153   * @param mode whether to include the content into the AuthenticatedData ({@link
154   *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
155   *             ({@link AuthenticatedDataStream#EXPLICIT explicit}) 
156   * @param keyEA the key encryption (key agreement) algorithm used for creating 
157   *              a shared key encryption key for encrypting the secret mac key with it
158   * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
159   *                   the mac key
160   * @param kekLength the length of the key encryption key to be created for
161   *                  encrypting the content encryption key with it     
162   *                                     
163   * @return the BER encoding of the <code>AuthenticatedData</code> object just created
164   * 
165   * @throws CMSException if the <code>AuthenticatedData</code> object cannot
166   *                          be created
167   * @throws IOException if an I/O error occurs
168   *
169   */
170  public byte[] createAuthenticatedDataStream(byte[] message,
171                                              AlgorithmID macAlgorithm,
172                                              int macKeyLength,
173                                              AlgorithmID digestAlgorithm,
174                                              int mode,
175                                              AlgorithmID keyEA, 
176                                              AlgorithmID keyWrapAlg,
177                                              int kekLength)
178    throws CMSException, IOException {
179
180    AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
181    AlgorithmID digestAlg = null;
182    if (digestAlgorithm != null) {
183       digestAlg = (AlgorithmID)digestAlgorithm.clone();
184    }   
185    ObjectID contentType = ObjectID.cms_data;
186    
187    AuthenticatedDataStream authenticatedData;
188
189    // we are testing the stream interface
190    ByteArrayInputStream is = new ByteArrayInputStream(message);
191    // create a new AuthenticatedData object 
192    try {
193      authenticatedData = new AuthenticatedDataStream(contentType,
194                                                      is, 
195                                                      macAlg,
196                                                      macKeyLength,
197                                                      null,
198                                                      digestAlg,
199                                                      mode);
200    } catch (NoSuchAlgorithmException ex) {
201      throw new CMSException(ex.toString());
202    }
203
204
205    // create the recipient infos
206    RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
207 // specify the recipients of the authenticated message
208    authenticatedData.setRecipientInfos(recipients);
209    
210    if (digestAlgorithm != null) {
211       // create some authenticated attributes
212       // (the message digest attribute is automatically added)
213       try {
214         Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
215         authenticatedData.setAuthenticatedAttributes(attributes);
216       } catch (Exception ex) {
217         throw new CMSException("Error creating attribute: " + ex.toString());   
218       } 
219    }    
220    
221    // in explicit mode get the content and write it  to any out-of-band place
222    if (mode == AuthenticatedDataStream.EXPLICIT) {
223      InputStream data_is = authenticatedData.getInputStream();
224      byte[] buf = new byte[2048];
225      int r;
226      while ((r = data_is.read(buf)) > 0)
227        ;   // skip data
228    }    
229      
230    // return the AuthenticatedData as BER encoded byte array with block size 16
231    // (just for testing; in real application we will use a proper blocksize,
232    //  e.g. 2048, 4096,..)
233    authenticatedData.setBlockSize(16);
234    // return the AuthenticatedDate as BER encoded byte array with block size 2048
235    ByteArrayOutputStream os = new ByteArrayOutputStream();
236    // wrap into ContentInfo
237    ContentInfoStream contentInfo = new ContentInfoStream(authenticatedData);
238    contentInfo.writeTo(os);
239    return os.toByteArray();
240  }
241  
242  /**
243   * Creates a CMS <code>AuthenticatedData</code> message using the 
244   * {@link iaik.cms.AuthenticatedDataOutputStream AuthenticatedDataOutputStream}
245   * class.
246   *
247   * @param message the message to be authenticated, as byte representation
248   * @param macAlgorithm the mac algorithm to be used
249   * @param macKeyLength the length of the temporary MAC key to be generated 
250   * @param digestAlgorithm the digest algorithm to be used to calculate a digest
251   *                        from the content if authenticated attributes should
252   *                        be included 
253   * @param mode whether to include the content into the AuthenticatedData ({@link
254   *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
255   *             ({@link AuthenticatedDataStream#EXPLICIT explicit}) 
256   * @param keyEA the key encryption (key agreement) algorithm used for creating 
257   *              a shared key encryption key for encrypting the secret mac key with it
258   * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
259   *                   the mac key
260   * @param kekLength the length of the key encryption key to be created for
261   *                  encrypting the content encryption key with it
262   *                                     
263   * @return the BER encoding of the <code>AuthenticatedData</code> object just created
264   * 
265   * @throws CMSException if the <code>AuthenticatedData</code> object cannot
266   *                          be created
267   * @throws IOException if an I/O error occurs
268   */
269  public byte[] createAuthenticatedDataOutputStream(byte[] message,
270                                                    AlgorithmID macAlgorithm,
271                                                    int macKeyLength,
272                                                    AlgorithmID digestAlgorithm,
273                                                    int mode,
274                                                    AlgorithmID keyEA, 
275                                                    AlgorithmID keyWrapAlg,
276                                                    int kekLength)
277    throws CMSException, IOException {
278
279    AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
280    AlgorithmID digestAlg = null;
281    if (digestAlgorithm != null) {
282       digestAlg = (AlgorithmID)digestAlgorithm.clone();
283    }   
284    ObjectID contentType = ObjectID.cms_data;
285    
286    AuthenticatedDataOutputStream authenticatedData;
287
288    //  a stream from which to read the data to be authenticated
289    ByteArrayInputStream is = new ByteArrayInputStream(message);
290    
291    // the stream to which to write the AuthenticatedData
292    ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
293
294    //  wrap AuthenticatedData into a ContentInfo 
295    ContentInfoOutputStream contentInfoStream = 
296      new ContentInfoOutputStream(ObjectID.cms_authData, resultStream);
297
298    // create AuthenticatedDataOutputStream 
299    try {
300      authenticatedData = new AuthenticatedDataOutputStream(contentType,
301                                                            contentInfoStream, 
302                                                            macAlg,
303                                                            macKeyLength,
304                                                            null,
305                                                            digestAlg,
306                                                            mode);
307    } catch (NoSuchAlgorithmException ex) {
308      throw new CMSException(ex.toString());
309    }
310
311
312    // create the recipient infos
313    RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
314    // specify the recipients of the authenticated message
315    authenticatedData.setRecipientInfos(recipients);
316    
317    if (digestAlgorithm != null) {
318       // create some authenticated attributes
319       // (the message digest attribute is automatically added)
320       try {
321         Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
322         authenticatedData.setAuthenticatedAttributes(attributes);
323       } catch (Exception ex) {
324         throw new CMSException("Error creating attribute: " + ex.toString());   
325       } 
326    }    
327    
328    int blockSize = 16; // in real world we would use a block size like 2048
329    //  write in the data to be signed
330    byte[] buffer = new byte[blockSize];
331    int bytesRead;
332    while ((bytesRead = is.read(buffer)) != -1) {
333      authenticatedData.write(buffer, 0, bytesRead);
334    }
335    
336    // closing the stream adds auth/unauth attributes, calculates and adds the mac value, . 
337    authenticatedData.close();
338    return resultStream.toByteArray();
339  }
340  
341
342  /**
343   * Decrypts the encrypted MAC key for the recipient identified by its index
344   * into the recipientInfos field and uses the MAC key to verify
345   * the authenticated data.
346   * <p>
347   * This way of decrypting the MAC key and verifying the content requires to 
348   * know at what index of the recipientInfos field the RecipientInfo for the 
349   * particular recipient in mind can be found. 
350   * For RecipientInfos of type KeyAgreeRecipientInfo some processing overhead may 
351   * take place because a KeyAgreeRecipientInfo may contain encrypted mac keys for
352   * more than only one recipient; since the recipientInfoIndex only specifies the
353   * RecipientInfo but not the encrypted mac key -- if there are more than only one --
354   * repeated decryption runs may be required as long as the decryption process 
355   * completes successfully.
356   *
357   * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
358   * @param message the content message, if transmitted by other means (explicit mode)
359   * @param key the key to decrypt the mac key 
360   * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
361   *                           which the given key belongs
362   *
363   * @return the verified message, as byte array
364   * 
365   * @throws CMSException if the authenticated data cannot be verified
366   * @throws IOException if a stream read/write error occurs
367   */
368  public byte[] getAuthenticatedDataStream(byte[] encoding, byte[] message, Key key, int recipientInfoIndex)
369    throws CMSException, IOException {
370
371 // create the AuthenticatedData object from a DER encoded byte array
372    // we are testing the stream interface
373    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
374    AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
375    
376    if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
377      // in explicit mode explicitly supply the content for hash/mac computation  
378      authenticatedData.setInputStream(new ByteArrayInputStream(message));
379    }
380
381    System.out.println("\nThis message can be verified by the following recipients:");
382    RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
383    
384    // for demonstration purposes we only look one time for all recipients included:
385    if (recipientInfoIndex == 0) {
386      int k = 0;
387      for (int i=0; i<recipients.length; i++) {
388        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
389        for (int j = 0; j < recipientIDs.length; j++) {
390          System.out.println("Recipient "+(++k)+":");
391          System.out.println(recipientIDs[j]);
392        }   
393      }
394    }
395    // decrypt the mac key and verify the mac for the indented recipient
396    try {
397      authenticatedData.setupMac(key, recipientInfoIndex);
398      InputStream contentStream = authenticatedData.getInputStream();
399      ByteArrayOutputStream os = new ByteArrayOutputStream();
400      Util.copyStream(contentStream, os, null);
401      
402      if (authenticatedData.verifyMac() == false) {
403        throw new CMSException("Mac verification error!");
404      }  
405      System.out.println("Mac successfully verified!");
406      
407      return os.toByteArray();
408
409    } catch (InvalidKeyException ex) {
410      throw new CMSException("Key error: "+ex.getMessage());
411    } catch (NoSuchAlgorithmException ex) {
412      throw new CMSException(ex.toString());
413    }
414
415  }
416  
417  /**
418   * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
419   * the recipient identified by recipient identifier.
420   * <p>
421   * This way of decrypting the content may be used for any type of RecipientInfo
422   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
423   * recipient in mind is identified by its recipient identifier.
424   *
425   * @param encoding the DER encoeded <code>AuthenticatedData</code> object#
426   * @param message the content message, if transmitted by other means (explicit mode)
427   * @param key the key to decrypt the message
428   * @param recipientID the recipient identifier uniquely identifying the key of the
429   *        recipient
430   *
431   * @return the recovered message, as byte array
432   * @throws CMSException if the message cannot be recovered
433   * @throws IOException if an I/O error occurs
434   */
435  public byte[] getAuthenticatedDataStream(byte[] encoding, byte[] message, Key key, KeyIdentifier recipientID)
436    throws CMSException, IOException {
437
438 // create the AuthenticatedData object from a DER encoded byte array
439    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
440    AuthenticatedData authenticatedData = new AuthenticatedData(is);
441    
442    if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
443      // in explicit mode explicitly supply the content for hash/mac computation  
444      authenticatedData.setContent(message);
445    }
446   
447    // get the right RecipientInfo
448    System.out.println("\nSearch for RecipientInfo:");
449    RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
450    if (recipient != null) {
451      System.out.println("RecipientInfo: " + recipient);   
452    } else {
453      throw new CMSException("No recipient with ID " + recipientID);
454    }
455    // decrypt the mac key and verify the content mac
456    try {
457      System.out.println("Decrypt encrypted mac key...");
458      SecretKey cek = recipient.decryptKey(key, recipientID);
459      System.out.println("Verify content mac with decrypted mac key...");
460      authenticatedData.setupMac(cek);
461      
462      if (authenticatedData.verifyMac() == false) {
463        throw new CMSException("Mac verification error!");
464      } 
465      System.out.println("Mac successfully verified!");
466
467      return authenticatedData.getContent();
468
469    } catch (InvalidKeyException ex) {
470      throw new CMSException("Key error: "+ex.getMessage());
471    } 
472  }
473  
474  /**
475   * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
476   * the recipient identified by its recipient certificate.
477   * <p>
478   *
479   * @param encoding the <code>AuthenticatedData</code> object as DER encoded byte array
480   * @param message the content message, if transmitted by other means (explicit mode) 
481   * @param key the key to decrypt the message
482   * @param recipientCert the certificate of the recipient having a RecipientInfo of
483   *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
484   *
485   * @return the recovered message, as byte array
486   * @throws CMSException if the message cannot be recovered
487   * @throws IOException if a stream read/write error occurs
488   */
489  public byte[] getAuthenticatedDataStream(byte[] encoding, byte[] message, Key key, X509Certificate recipientCert)
490    throws CMSException, IOException {
491
492    // create the AuthenticatedData object from a DER encoded byte array
493    // we are testing the stream interface
494    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
495    AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
496    
497    if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
498      // in explicit mode explicitly supply the content for hash/mac computation  
499      authenticatedData.setInputStream(new ByteArrayInputStream(message));
500    }
501
502   
503    // decrypt the mac key and verify the content mac
504    try {
505      System.out.println("Verify mac...");
506      authenticatedData.setupMac(key, recipientCert);
507     
508      InputStream contentStream = authenticatedData.getInputStream();
509      ByteArrayOutputStream os = new ByteArrayOutputStream();
510      Util.copyStream(contentStream, os, null);
511      
512      if (authenticatedData.verifyMac() == false) {
513        throw new CMSException("Mac verification error!");
514      }
515      System.out.println("Mac successfully verified!");
516
517      return os.toByteArray();
518
519    } catch (InvalidKeyException ex) {
520      throw new CMSException("Key error: "+ex.getMessage());
521    } catch (NoSuchAlgorithmException ex) {
522      throw new CMSException(ex.toString());
523    }
524  }
525
526
527  // non stream
528
529  /**
530   * Creates a CMS <code>AuthenticatedData</code> for the given message message.
531   *
532   * @param message the message to be authenticated, as byte representation
533   * @param macAlgorithm the mac algorithm to be used
534   * @param macKeyLength the length of the temporary MAC key to be generated 
535   * @param digestAlgorithm the digest algorithm to be used to calculate a digest
536   *                        from the content if authenticated attributes should
537   *                        be included 
538   * @param mode whether to include the content into the AuthenticatedData ({@link
539   *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
540   *             ({@link AuthenticatedDataStream#EXPLICIT explicit}) 
541   * @param keyEA the key encryption (key agreement) algorithm used for creating 
542   *              a shared key encryption key for encrypting the secret mac key with it
543   * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
544   *                   the mac key
545   * @param kekLength the length of the key encryption key to be created for
546   *                  encrypting the content encryption key with it
547   *                                    
548   * @return the BER encoding of the <code>AuthenticatedData</code> object just created
549   * 
550   * @throws CMSException if the <code>AuthenticatedData</code> object cannot
551   *                          be created
552   */
553  public byte[] createAuthenticatedData(byte[] message,
554                                        AlgorithmID macAlgorithm,
555                                        int macKeyLength,
556                                        AlgorithmID digestAlgorithm,
557                                        int mode,
558                                        AlgorithmID keyEA, 
559                                        AlgorithmID keyWrapAlg,
560                                        int kekLength)
561    throws CMSException {
562    
563    AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
564    AlgorithmID digestAlg = null;
565    if (digestAlgorithm != null) {
566       digestAlg = (AlgorithmID)digestAlgorithm.clone();
567    }   
568    ObjectID contentType = ObjectID.cms_data;
569    
570    AuthenticatedData authenticatedData;
571
572    // create a new AuthenticatedData object 
573    try {
574      authenticatedData = new AuthenticatedData(contentType,
575                                                message, 
576                                                macAlg,
577                                                macKeyLength,
578                                                null,
579                                                digestAlg,
580                                                mode);
581    } catch (NoSuchAlgorithmException ex) {
582      throw new CMSException(ex.toString());
583    }
584
585    
586    // set the RecipientInfos
587    RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
588    authenticatedData.setRecipientInfos(recipients);
589    
590    if (digestAlgorithm != null) {
591       // create some authenticated attributes
592       // (the message digest attribute is automatically added)
593       try {
594         Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
595         authenticatedData.setAuthenticatedAttributes(attributes);
596       } catch (Exception ex) {
597         throw new CMSException("Error creating attribute: " + ex.toString());   
598       } 
599    }    
600   
601    // wrap into ContentInfo
602    ContentInfo contentInfo = new ContentInfo(authenticatedData);
603    // return encoded EnvelopedData
604    return contentInfo.getEncoded();
605  }
606
607
608  /**
609   * Decrypts the encrypted MAC key for the recipient identified by its index
610   * into the recipientInfos field and uses the MAC key to verify
611   * the authenticated data.
612   * <p>
613   * This way of decrypting the MAC key and verifying the content requires 
614   * to know at what index of the recipientInfos field the RecipientInfo for 
615   * the particular recipient in mind can be found. 
616   * For RecipientInfos of type KeyAgreeRecipientInfo some processing overhead 
617   * may take place because a KeyAgreeRecipientInfo may contain encrypted mac 
618   * keys for more than only one recipient; since the recipientInfoIndex only 
619   * specifies the RecipientInfo but not the encrypted
620   * mac key -- if there are more than only one -- repeated decryption runs may be
621   * required as long as the decryption process completes successfully.
622   *
623   * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
624   * @param message the content message, if transmitted by other means (explicit mode)
625   * @param key the key to decrypt the mac key
626   * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
627   *                           which the given key belongs
628   *
629   * @return the verified message, as byte array
630   * @throws CMSException if the authenticated data cannot be verified
631   * @throws IOException if a IO read/write error occurs
632   */
633  public byte[] getAuthenticatedData(byte[] encoding, byte[] message, Key key, int recipientInfoIndex) 
634    throws CMSException, IOException {
635    // create the AuthenticatedData object from a DER encoded byte array
636    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
637    AuthenticatedData authenticatedData = new AuthenticatedData(is);
638    
639    if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
640      // in explicit mode explicitly supply the content for hash/mac computation  
641      authenticatedData.setContent(message);
642    }
643
644    System.out.println("\nThis message can be verified by the owners of the following recipients:");
645    RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
646    
647    // for demonstration purposes we only look one time for all recipients included:
648    if (recipientInfoIndex == 0) {
649      int k = 0;
650      for (int i=0; i<recipients.length; i++) {
651        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
652        for (int j = 0; j < recipientIDs.length; j++) {
653          System.out.println("Recipient "+(++k)+":");
654          System.out.println(recipientIDs[j]);
655        }   
656      }
657    }
658    // decrypt the mac key and verify the mac for the first recipient
659    try {
660      authenticatedData.setupMac(key, recipientInfoIndex);
661      if (authenticatedData.verifyMac() == false) {
662        throw new CMSException("Mac verification error!");
663      }  
664      System.out.println("Mac successfully verified!");
665      
666      return authenticatedData.getContent();
667
668    } catch (InvalidKeyException ex) {
669      throw new CMSException("Key error: "+ex.getMessage());
670    } catch (NoSuchAlgorithmException ex) {
671      throw new CMSException(ex.toString());
672    }
673  }
674  
675  /**
676   * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
677   * the recipient identified by recipient identifier.
678   * <p>
679   * This way of decrypting the content may be used for any type of RecipientInfo
680   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
681   * recipient in mind is identified by its recipient identifier.
682   *
683   * @param encoding the DER encoeded <code>AuthenticatedData</code> object#
684   * @param message the content message, if transmitted by other means (explicit mode)
685   * @param key the key to decrypt the message
686   * @param recipientID the recipient identifier uniquely identifying the key of the
687   *        recipient
688   *
689   * @return the recovered message, as byte array
690   * @throws CMSException if the message cannot be recovered
691   * @throws IOException if an I/O error occurs
692   */
693  public byte[] getAuthenticatedData(byte[] encoding, byte[] message, Key key, KeyIdentifier recipientID) 
694    throws CMSException, IOException {
695 // create the AuthenticatedData object from a DER encoded byte array
696    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
697    AuthenticatedData authenticatedData = new AuthenticatedData(is);
698    
699    if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
700      // in explicit mode explicitly supply the content for hash/mac computation  
701      authenticatedData.setContent(message);
702    }
703   
704    // get the right RecipientInfo
705    System.out.println("\nSearch for RecipientInfo:");
706    RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
707    if (recipient != null) {
708      System.out.println("RecipientInfo: " + recipient);   
709    } else {
710      throw new CMSException("No recipient with ID " + recipientID);
711    }
712    // decrypt the mac key and verify the content mac
713    try {
714      System.out.println("Decrypt encrypted mac key...");
715      SecretKey cek = recipient.decryptKey(key, recipientID);
716      System.out.println("Verify content mac with decrypted mac key...");
717      authenticatedData.setupMac(cek);
718      
719      if (authenticatedData.verifyMac() == false) {
720        throw new CMSException("Mac verification error!");
721      } 
722      System.out.println("Mac successfully verified!");
723
724      return authenticatedData.getContent();
725
726    } catch (InvalidKeyException ex) {
727      throw new CMSException("Key error: "+ex.getMessage());
728    } 
729  }
730  
731  /**
732   * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
733   * the recipient identified by its recipient certificate.
734   * <p>
735   *
736   * @param encoding the DER encoded <code>AuthenticatedData</code> ASN.1 object
737   * @param message the content message, if transmitted by other means (explicit mode) 
738   * @param key the key to decrypt the message
739   * @param recipientCert the certificate of the recipient having a RecipientInfo of
740   *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
741   *
742   * @return the recovered message, as byte array
743   * @throws CMSException if the message cannot be recovered
744   */
745  public byte[] getAuthenticatedData(byte[] encoding, byte[] message, Key key, X509Certificate recipientCert) 
746    throws CMSException, IOException {
747    // create the AuthenticatedData object from a DER encoded byte array
748    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
749    AuthenticatedData authenticatedData = new AuthenticatedData(is);
750    
751    if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
752      // in explicit mode explicitly supply the content for hash/mac computation  
753      authenticatedData.setContent(message);
754    }
755
756    // decrypt the mac key and verify the content mac
757    try {
758      System.out.println("Verify mac...");
759      authenticatedData.setupMac(key, recipientCert);
760     
761      if (authenticatedData.verifyMac() == false) {
762        throw new CMSException("Mac verification error!");
763      }
764      System.out.println("Mac successfully verified!");
765
766      return authenticatedData.getContent();
767      
768    } catch (InvalidKeyException ex) {
769      throw new CMSException("Key error: "+ex.getMessage());
770    } catch (NoSuchAlgorithmException ex) {
771      throw new CMSException(ex.toString());
772    } 
773  }
774  
775  /**
776   * Creates the RecipientInfos.
777   * 
778   * @param keyEA the key encryption (key agreement) algorithm used for creating 
779   *              a shared key encryption key for encrypting the secret content
780   *              encryption key with it
781   * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
782   *                   the content encryption key
783   * @param kekLength the length of the key encryption key to be created for
784   *                  encrypting the content encryption key with it              
785   *
786   * @return the RecipientInfos created, two KeyAgreeRecipientInfos
787   *
788   * @throws CMSException if an error occurs when creating the recipient infos
789   */
790  public RecipientInfo[] createRecipients(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException {
791    // just for demonstration we use two recipients, one having a x25519 key, the other a x448 key
792    RecipientInfo[] recipients = new RecipientInfo[2];
793    try {
794      recipients[0] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
795      ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(x25519User_, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
796  
797      recipients[1] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
798      ((KeyAgreeRecipientInfo)recipients[1]).addRecipient(x448User_, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
799      
800    } catch (Exception ex) {
801      throw new CMSException("Error adding recipients: " + ex.toString()); 
802    }    
803    return recipients;
804  }  
805  
806  /**
807   * Parses an AuthenticatedData and decrypts the content for all test recipients
808   * using the index into the recipientInfos field for identifying the recipient.
809   *
810   * @param stream whether to use AuthenticatedDataStream or AuthenticatedData
811   * @param encodedAuthenticatedData the encoded AuthenticatedData object 
812   * @param message the content message, if transmitted by other means (explicit mode)
813   *
814   * @throws Exception if some error occurs during mac key decryption / mac verification
815   */  
816  public void parseAuthenticatedDataWithRecipientInfoIndex(boolean stream, 
817    byte[] encodedAuthenticatedData, byte[] message) throws Exception {
818    byte[] receivedMessage;
819    if (stream) {
820      // x25519User
821      System.out.println("\nVerify MAC for x25519User:");
822      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x25519UserPk_, 0);
823      System.out.print("\nContent: ");
824      System.out.println(new String(receivedMessage));
825      // x448User
826      System.out.println("\nVerify MAC for x448User:");
827      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x448UserPk_, 1);
828      System.out.print("\nContent: ");
829      System.out.println(new String(receivedMessage));
830
831    } else {
832      // x25519User
833      System.out.println("\nVerify MAC for x25519User:");
834      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x25519UserPk_, 0);
835      System.out.print("\nContent: ");
836      System.out.println(new String(receivedMessage));
837      // x448User
838      System.out.println("\nVerify MAC for x448User:");
839      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x448UserPk_, 1);
840      System.out.print("\nContent: ");
841      System.out.println(new String(receivedMessage));
842
843    }    
844  }
845  
846  /**
847   * Parses an AuthenticatedData, decrypts the mac keys for all test recipients
848   * using their recipient identifiers for identifying the recipient
849   * and verifies the content mac.
850   *
851   * @param stream whether to use AuthenticatedDataStream or AuthenticatedData
852   * @param encodedAuthenticatedData the encoded AuthenticatedData object 
853   * @param message the content message, if transmitted by other means (explicit mode)  
854   *
855   * @throws Exception if some error occurs during mac key decryption / mac verification
856   */ 
857  public void parseAuthenticatedDataWithRecipientIdentifier(boolean stream, 
858    byte[] encodedAuthenticatedData, byte[] message) throws Exception {
859    
860    byte[] receivedMessage;
861    if (stream) {
862      // x25519User
863      System.out.println("\nVerify MAC for x25519User:");
864      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x25519UserPk_, new IssuerAndSerialNumber(x25519User_));
865      System.out.print("\nContent: ");
866      System.out.println(new String(receivedMessage));
867      // x448User
868      System.out.println("\nVerify MAC for x448User:");
869      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x448UserPk_, new RecipientKeyIdentifier(x448User_));
870      System.out.print("\nContent: ");
871      System.out.println(new String(receivedMessage));
872    } else {
873      // x25519User
874      System.out.println("\nVerify MAC for x25519User:");
875      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x25519UserPk_, new IssuerAndSerialNumber(x25519User_));
876      System.out.print("\nContent: ");
877      System.out.println(new String(receivedMessage));
878      // x448User
879      System.out.println("\nVerify MAC for x448User:");
880      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x448UserPk_, new RecipientKeyIdentifier(x448User_));
881      System.out.print("\nContent: ");
882      System.out.println(new String(receivedMessage));
883    }    
884  }
885  
886  /**
887   * Parses an AuthenticatedData and decrypts the content for all test recipients
888   * using their recipient certificate for identifying the recipient.
889   *
890   * @param stream whether to use AuthenticatedDataStream or AuthenticatedDatas
891   * @param encodedAuthenticatedData the encoded AuthenticatedData object 
892   *
893   * @throws Exception if some error occurs during decoding/decryption
894   */ 
895  public void parseAuthenticatedDataWithRecipientCert(boolean stream, byte[] encodedAuthenticatedData, byte[] message) throws Exception {
896    byte[] receivedMessage;
897    if (stream) {
898      // x25519User
899      System.out.println("\nVerify MAC for x25519User:");
900      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x25519UserPk_, x25519User_);
901      System.out.print("\nContent: ");
902      System.out.println(new String(receivedMessage));
903      // x448User
904      System.out.println("\nVerify MAC for x448User:");
905      receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, x448UserPk_, x448User_);
906      System.out.print("\nContent: ");
907      System.out.println(new String(receivedMessage));
908    } else {
909      // x25519User
910      System.out.println("\nVerify MAC for x25519User:");
911      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x25519UserPk_, x25519User_);
912      System.out.print("\nContent: ");
913      System.out.println(new String(receivedMessage));
914      // x448User
915      System.out.println("\nVerify MAC for x448User:");
916      receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, x448UserPk_, x448User_);
917      System.out.print("\nContent: ");
918      System.out.println(new String(receivedMessage));
919    }    
920  }
921  
922  /**
923   * Starts the test.
924   */
925  public void start() {
926  
927    AlgorithmID[] keyEAs = {
928        AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme, 
929        AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme, 
930        AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme, 
931        AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme,
932        AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme, 
933    };
934    
935    AlgorithmID keyWrapAlg = CMSAlgorithmID.cms_HMACwithAES_wrap;
936    int kekLength = 128;
937    
938    for (int i = 0; i < keyEAs.length; i++) {
939      AlgorithmID keyEA = keyEAs[i];
940      System.out.println("EdDH AuthenticatedData demo for " + keyEA.getName() + " with " + keyWrapAlg.getName());
941      start(keyEA, keyWrapAlg, kekLength);
942    } 
943  }
944  
945  /**
946   * Starts the test for the given content-authenticated encryption algorithm.
947   * 
948   * @param keyEA the key encryption (key agreement) algorithm used for creating 
949   *              a shared key encryption key for encrypting the secret content
950   *              encryption key with it
951   * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting) 
952   *                   the content encryption key
953   * @param kekLength the length of the key encryption key to be created for
954   *                  encrypting the content encryption key with it       
955   */
956  public void start(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) {
957   
958    AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
959    int macKeyLength = 32;
960    AlgorithmID digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
961    
962    // the test message
963    String m = "This is the test message.";
964    System.out.println("Test message: \""+m+"\"");
965    System.out.println();
966    byte[] message = m.getBytes();
967
968    try {
969      byte[] encodedAuthenticatedData;
970      System.out.println("Stream implementation demos");
971      System.out.println("===========================");
972
973      // the stream implementation
974      //
975      // test CMS AuthenticatedDataStream
976      //
977      
978      // implicit mode; with authenticated attributes
979      System.out.println("\nCMS AuthenticatedDataStream demo with authenticated attributes [create, implicit mode]:\n");
980      encodedAuthenticatedData = createAuthenticatedDataStream(message, 
981                                                               macAlgorithm,
982                                                               macKeyLength,
983                                                               digestAlgorithm,
984                                                               AuthenticatedDataStream.IMPLICIT,
985                                                               keyEA, 
986                                                               keyWrapAlg, 
987                                                               kekLength);
988      // transmit data
989      System.out.println("\nCMS AuthenticatedDataStream demo [parse, implicit mode]:\n");
990      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
991      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, null);
992      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
993      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, null);
994      System.out.println("Verify MAC for the several recipients using their certificate.");
995      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, null);
996      
997      // implicit mode; without authenticated attributes
998      System.out.println("\nCMS AuthenticatedDataStream demo without authenticated attributes [create, implicit mode]:\n");
999      encodedAuthenticatedData = createAuthenticatedDataStream(message, 
1000                                                               macAlgorithm,
1001                                                               macKeyLength,
1002                                                               null,
1003                                                               AuthenticatedDataStream.IMPLICIT,
1004                                                               keyEA, 
1005                                                               keyWrapAlg, 
1006                                                               kekLength);
1007      // transmit data
1008      System.out.println("\nCMS AuthenticatedDataStream demo [parse, implicit mode]:\n");
1009      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1010      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, null);
1011      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1012      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, null);
1013      System.out.println("Verify MAC for the several recipients using their certificate.");
1014      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, null);
1015      
1016      
1017      // explicit mode; with authenticated attributes
1018      System.out.println("\nCMS AuthenticatedDataStream demo without authenticated attributes [create, implicit mode]:\n");
1019      encodedAuthenticatedData = createAuthenticatedDataStream(message, 
1020                                                               macAlgorithm,
1021                                                               macKeyLength,
1022                                                               digestAlgorithm,
1023                                                               AuthenticatedDataStream.EXPLICIT,
1024                                                               keyEA, 
1025                                                               keyWrapAlg, 
1026                                                               kekLength);
1027      // transmit data
1028      System.out.println("\nCMS AuthenticatedDataStream demo [parse, implicit mode]:\n");
1029      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1030      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, message);
1031      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1032      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, message);
1033      System.out.println("Verify MAC for the several recipients using their certificate.");
1034      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, message);
1035      
1036      // explicit mode; without authenticated attributes
1037      System.out.println("\nCMS AuthenticatedDataStream demo without authenticated attributes [create, implicit mode]:\n");
1038      encodedAuthenticatedData = createAuthenticatedDataStream(message, 
1039                                                               macAlgorithm,
1040                                                               macKeyLength,
1041                                                               null,
1042                                                               AuthenticatedDataStream.EXPLICIT,
1043                                                               keyEA, 
1044                                                               keyWrapAlg, 
1045                                                               kekLength);
1046      // transmit data
1047      System.out.println("\nCMS AuthenticatedDataStream demo [parse, implicit mode]:\n");
1048      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1049      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, message);
1050      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1051      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, message);
1052      System.out.println("Verify MAC for the several recipients using their certificate.");
1053      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, message);
1054      
1055      // the output stream implementation
1056      System.out.println("OutputStream implementation demos");
1057      System.out.println("=================================");
1058      
1059      //
1060      // test CMS AuthenticatedDataOutputStream
1061      //
1062      System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, implicit mode]:\n");
1063      encodedAuthenticatedData = createAuthenticatedDataOutputStream(message, 
1064                                                                     macAlgorithm,
1065                                                                     macKeyLength,
1066                                                                     digestAlgorithm,
1067                                                                     AuthenticatedDataStream.IMPLICIT,
1068                                                                     keyEA, 
1069                                                                     keyWrapAlg, 
1070                                                                     kekLength);
1071      // transmit data
1072      System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
1073      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1074      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, null);
1075      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1076      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, null);
1077      System.out.println("Verify MAC for the several recipients using their certificate.");
1078      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, null);
1079      
1080      // implicit mode; without authenticated attributes
1081      System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n");
1082      encodedAuthenticatedData = createAuthenticatedDataOutputStream(message, 
1083                                                                     macAlgorithm,
1084                                                                     macKeyLength,
1085                                                                     null,
1086                                                                     AuthenticatedDataStream.IMPLICIT,
1087                                                                     keyEA, 
1088                                                                     keyWrapAlg, 
1089                                                                     kekLength);
1090      // transmit data
1091      System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
1092      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1093      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, null);
1094      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1095      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, null);
1096      System.out.println("Verify MAC for the several recipients using their certificate.");
1097      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, null);
1098      
1099      
1100      // explicit mode; with authenticated attributes
1101      System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n");
1102      encodedAuthenticatedData = createAuthenticatedDataOutputStream(message, 
1103                                                                     macAlgorithm,
1104                                                                     macKeyLength,
1105                                                                     digestAlgorithm,
1106                                                                     AuthenticatedDataStream.EXPLICIT,
1107                                                                     keyEA, 
1108                                                                     keyWrapAlg, 
1109                                                                     kekLength);
1110      // transmit data
1111      System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
1112      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1113      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, message);
1114      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1115      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, message);
1116      System.out.println("Verify MAC for the several recipients using their certificate.");
1117      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, message);
1118      
1119      // explicit mode; without authenticated attributes
1120      System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n");
1121      encodedAuthenticatedData = createAuthenticatedDataOutputStream(message, 
1122                                                                     macAlgorithm,
1123                                                                     macKeyLength,
1124                                                                     null,
1125                                                                     AuthenticatedDataStream.EXPLICIT,
1126                                                                     keyEA, 
1127                                                                     keyWrapAlg, 
1128                                                                     kekLength);
1129      // transmit data
1130      System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
1131      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1132      parseAuthenticatedDataWithRecipientInfoIndex(true, encodedAuthenticatedData, message);
1133      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1134      parseAuthenticatedDataWithRecipientIdentifier(true, encodedAuthenticatedData, message);
1135      System.out.println("Verify MAC for the several recipients using their certificate.");
1136      parseAuthenticatedDataWithRecipientCert(true, encodedAuthenticatedData, message);
1137
1138      // the non-stream implementation
1139      System.out.println("\nNon-stream implementation demos");
1140      System.out.println("===============================");
1141
1142      //
1143      // test CMS AuthenticatedData
1144      //
1145      
1146      // implicit mode; with authenticated attributes
1147      System.out.println("\nCMS AuthenticatedData demo with authenticated attributes [create, implicit mode]:\n");
1148      encodedAuthenticatedData = createAuthenticatedData(message, 
1149                                                         macAlgorithm,
1150                                                         macKeyLength,
1151                                                         digestAlgorithm,
1152                                                         AuthenticatedDataStream.IMPLICIT,
1153                                                         keyEA, 
1154                                                         keyWrapAlg, 
1155                                                         kekLength);
1156      // transmit data
1157      System.out.println("\nCMS AuthenticatedData demo [parse, implicit mode]:\n");
1158      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1159      parseAuthenticatedDataWithRecipientInfoIndex(false, encodedAuthenticatedData, null);
1160      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1161      parseAuthenticatedDataWithRecipientIdentifier(false, encodedAuthenticatedData, null);
1162      System.out.println("Verify MAC for the several recipients using their certificate.");
1163      parseAuthenticatedDataWithRecipientCert(false, encodedAuthenticatedData, null);
1164      
1165      // implicit mode; without authenticated attributes
1166      System.out.println("\nCMS AuthenticatedData demo without authenticated attributes [create, implicit mode]:\n");
1167      encodedAuthenticatedData = createAuthenticatedData(message, 
1168                                                         macAlgorithm,
1169                                                         macKeyLength,
1170                                                         null,
1171                                                         AuthenticatedDataStream.IMPLICIT,
1172                                                         keyEA, 
1173                                                         keyWrapAlg, 
1174                                                         kekLength);
1175      // transmit data
1176      System.out.println("\nCMS AuthenticatedData demo [parse, implicit mode]:\n");
1177      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1178      parseAuthenticatedDataWithRecipientInfoIndex(false, encodedAuthenticatedData, null);
1179      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1180      parseAuthenticatedDataWithRecipientIdentifier(false, encodedAuthenticatedData, null);
1181      System.out.println("Verify MAC for the several recipients using their certificate.");
1182      parseAuthenticatedDataWithRecipientCert(false, encodedAuthenticatedData, null);
1183      
1184      
1185      // explicit mode; with authenticated attributes
1186      System.out.println("\nCMS AuthenticatedData demo without authenticated attributes [create, implicit mode]:\n");
1187      encodedAuthenticatedData = createAuthenticatedData(message, 
1188                                                         macAlgorithm,
1189                                                         macKeyLength,
1190                                                         digestAlgorithm,
1191                                                         AuthenticatedDataStream.EXPLICIT,
1192                                                         keyEA, 
1193                                                         keyWrapAlg, 
1194                                                         kekLength);
1195      // transmit data
1196      System.out.println("\nCMS AuthenticatedData demo [parse, implicit mode]:\n");
1197      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1198      parseAuthenticatedDataWithRecipientInfoIndex(false, encodedAuthenticatedData, message);
1199      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1200      parseAuthenticatedDataWithRecipientIdentifier(false, encodedAuthenticatedData, message);
1201      System.out.println("Verify MAC for the several recipients using their certificate.");
1202      parseAuthenticatedDataWithRecipientCert(false, encodedAuthenticatedData, message);
1203      
1204      // explicit mode; without authenticated attributes
1205      System.out.println("\nCMS AuthenticatedData demo without authenticated attributes [create, implicit mode]:\n");
1206      encodedAuthenticatedData = createAuthenticatedData(message, 
1207                                                         macAlgorithm,
1208                                                         macKeyLength,
1209                                                         null,
1210                                                         AuthenticatedDataStream.EXPLICIT,
1211                                                         keyEA, 
1212                                                         keyWrapAlg, 
1213                                                         kekLength);
1214      // transmit data
1215      System.out.println("\nCMS AuthenticatedData demo [parse, implicit mode]:\n");
1216      System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
1217      parseAuthenticatedDataWithRecipientInfoIndex(false, encodedAuthenticatedData, message);
1218      System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
1219      parseAuthenticatedDataWithRecipientIdentifier(false, encodedAuthenticatedData, message);
1220      System.out.println("Verify MAC for the several recipients using their certificate.");
1221      parseAuthenticatedDataWithRecipientCert(false, encodedAuthenticatedData, message); 
1222
1223        } catch (Exception ex) {
1224          ex.printStackTrace();
1225          throw new RuntimeException(ex.toString());
1226        }
1227  }
1228  
1229  
1230  /**
1231   * Main method.
1232   *
1233   * @throws IOException
1234   *            if an I/O error occurs when reading required keys
1235   *            and certificates from files
1236   */
1237  public static void main(String argv[]) throws Exception {
1238
1239    DemoUtil.initDemos();
1240    ECCDemoUtil.installIaikEccProvider();
1241
1242    (new EdDHAuthenticatedDataDemo()).start();
1243    System.out.println("\nReady!");
1244    DemoUtil.waitKey();
1245  }
1246  
1247}