001    // Copyright (C) 2002 IAIK
002    // https://jce.iaik.tugraz.at
003    //
004    // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005    //                           Communication Technologies SIC
006    // https://sic.tech
007    //
008    // All rights reserved.
009    //
010    // Redistribution and use in source and binary forms, with or without
011    // modification, are permitted provided that the following conditions
012    // are met:
013    // 1. Redistributions of source code must retain the above copyright
014    //    notice, this list of conditions and the following disclaimer.
015    // 2. Redistributions in binary form must reproduce the above copyright
016    //    notice, this list of conditions and the following disclaimer in the
017    //    documentation and/or other materials provided with the distribution.
018    //
019    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029    // SUCH DAMAGE.
030    
031    // Copyright (C) 2002 IAIK
032    // https://sic.tech/
033    //
034    // Copyright (C) 2003 - 2025 Stiftung Secure Information and 
035    //                           Communication Technologies SIC
036    // https://sic.tech/
037    //
038    // All rights reserved.
039    //
040    // This source is provided for inspection purposes and recompilation only,
041    // unless specified differently in a contract with IAIK. This source has to
042    // be kept in strict confidence and must not be disclosed to any third party
043    // under any circumstances. Redistribution in source and binary forms, with
044    // or without modification, are <not> permitted in any case!
045    //
046    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056    // SUCH DAMAGE.
057    //
058    // $Header: /IAIK-CMS/current/src/demo/cms/ecc/ECDHAuthenticatedDataDemo.java 5     12.02.25 17:58 Dbratko $
059    // $Revision: 5 $
060    //
061    
062    
063    package demo.cms.ecc;
064    
065    import iaik.asn1.ObjectID;
066    import iaik.asn1.structures.AlgorithmID;
067    import iaik.asn1.structures.Attribute;
068    import iaik.cms.AuthenticatedData;
069    import iaik.cms.AuthenticatedDataOutputStream;
070    import iaik.cms.AuthenticatedDataStream;
071    import iaik.cms.CMSException;
072    import iaik.cms.ContentInfo;
073    import iaik.cms.ContentInfoOutputStream;
074    import iaik.cms.ContentInfoStream;
075    import iaik.cms.IssuerAndSerialNumber;
076    import iaik.cms.KeyIdentifier;
077    import iaik.cms.OriginatorInfo;
078    import iaik.cms.RecipientInfo;
079    import iaik.cms.RecipientKeyIdentifier;
080    import iaik.cms.attributes.CMSContentType;
081    import iaik.utils.Util;
082    import iaik.x509.X509Certificate;
083    
084    import java.io.ByteArrayInputStream;
085    import java.io.ByteArrayOutputStream;
086    import java.io.IOException;
087    import java.io.InputStream;
088    import java.security.InvalidKeyException;
089    import java.security.Key;
090    import java.security.NoSuchAlgorithmException;
091    
092    import javax.crypto.SecretKey;
093    
094    import demo.DemoUtil;
095    
096    /**
097     * Demonstrates the usage of class {@link iaik.cms.AuthenticatedDataStream}, 
098     * {@link iaik.cms.AuthenticatedData} and {@link iaik.cms.AuthenticatedDataOutputStream}
099     * for authenticated encrypting data using the CMS type
100     * AuthenticatedData by using Static-Static ECDH according to <a href = 
101     * "http://www.ietf.org/rfc/rfc6278.txt" target="_blank">6278</a> as 
102     * key agreement method.
103     * <p>
104     * Any keys/certificates required for this demo are read from a keystore
105     * file "cmsecc.keystore" located in your current working directory. If
106     * the keystore file does not exist you can create it by running the
107     * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
108     * program. 
109     * <p>
110     * Additionally to <code>iaik_cms.jar</code> you also must have 
111     * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
112     * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
113     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
114     * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
115     * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
116     * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
117     * in your classpath.
118     *
119     * @see iaik.cms.AuthenticatedDataStream
120     * @see iaik.cms.AuthenticatedData
121     * @see iaik.cms.AuthenticatedDataOutputStream
122     * @see iaik.cms.RecipientInfo
123     * @see iaik.cms.KeyAgreeRecipientInfo
124     * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore
125     */
126    public class ECDHAuthenticatedDataDemo extends StaticStaticECDHDemo {
127    
128      /**
129       * Setup the demo certificate chains.
130       *
131       * Keys and certificates are retrieved from the demo keyStore file
132       * "cmsecc.keystore" located in your current working directory. If
133       * the keystore file does not exist you can create it by running the
134       * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
135       * program. 
136       *
137       * @throws IOException if keys/certificates cannot be read from the keystore
138       */
139      public ECDHAuthenticatedDataDemo() throws IOException {
140        super();
141        System.out.println();
142        System.out.println("**********************************************************************************");
143        System.out.println("*                          ECDH AuthenticatedData demo                           *");
144        System.out.println("*   (shows the usage of the CMS AuthenticatedData type implementation for ECDH)  *");
145        System.out.println("**********************************************************************************");
146        System.out.println();
147        
148        
149      }
150    
151      /**
152       * Creates a CMS <code>AuthenticatedDataStream</code> for the given message message.
153       *
154       * @param message the message to be authenticated, as byte representation
155       * @param macAlgorithm the mac algorithm to be used
156       * @param macKeyLength the length of the temporary MAC key to be generated
157       * @param digestAlgorithm the digest algorithm to be used to calculate a digest
158       *                        from the content if authenticated attributes should
159       *                        be included
160       * @param mode whether to include the content into the AuthenticatedData ({@link
161       *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
162       *             ({@link AuthenticatedDataStream#EXPLICIT explicit})
163       *                                    
164       * @return the BER encoding of the <code>AuthenticatedData</code> object just created
165       * 
166       * @throws CMSException if the <code>AuthenticatedData</code> object cannot
167       *                          be created
168       * @throws IOException if an I/O error occurs
169       */
170      public byte[] createAuthenticatedDataStream(byte[] message,
171                                                  AlgorithmID macAlgorithm,
172                                                  int macKeyLength,
173                                                  AlgorithmID digestAlgorithm,
174                                                  int mode)
175        throws CMSException, IOException {
176        
177        AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
178        AlgorithmID digestAlg = null;
179        if (digestAlgorithm != null) {
180           digestAlg = (AlgorithmID)digestAlgorithm.clone();
181        }   
182        ObjectID contentType = ObjectID.cms_data;
183        
184        AuthenticatedDataStream authenticatedData;
185    
186        // we are testing the stream interface
187        ByteArrayInputStream is = new ByteArrayInputStream(message);
188        // create a new AuthenticatedData object 
189        try {
190          authenticatedData = new AuthenticatedDataStream(contentType,
191                                                          is, 
192                                                          macAlg,
193                                                          macKeyLength,
194                                                          null,
195                                                          digestAlg,
196                                                          mode);
197        } catch (NoSuchAlgorithmException ex) {
198          throw new CMSException(ex.toString());
199        }
200    
201        // static-static mode: set OriginatorInfo
202        OriginatorInfo originator = new OriginatorInfo();
203        originator.setCertificates(ecdhOriginatorCerts_);
204        authenticatedData.setOriginatorInfo(originator);
205        // create the recipient infos
206        RecipientInfo[] recipients = createRecipients();
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          
231        // return the AuthenticatedData as BER encoded byte array with block size 16
232        // (just for testing; in real application we will use a proper blocksize,
233        //  e.g. 2048, 4096,..)
234        authenticatedData.setBlockSize(16);
235        // return the AuthenticatedDate as BER encoded byte array with block size 2048
236        ByteArrayOutputStream os = new ByteArrayOutputStream();
237        // wrap into ContentInfo
238        ContentInfoStream contentInfo = new ContentInfoStream(authenticatedData);
239        contentInfo.writeTo(os);
240        return os.toByteArray();
241      }
242      
243      /**
244       * Creates a CMS <code>AuthenticatedDataOutputStream</code> for the given message message.
245       *
246       * @param message the message to be authenticated, as byte representation
247       * @param macAlgorithm the mac algorithm to be used
248       * @param macKeyLength the length of the temporary MAC key to be generated
249       * @param digestAlgorithm the digest algorithm to be used to calculate a digest
250       *                        from the content if authenticated attributes should
251       *                        be included
252       * @param mode whether to include the content into the AuthenticatedData ({@link
253       *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
254       *             ({@link AuthenticatedDataStream#EXPLICIT explicit})
255       *                                     
256       * @return the BER encoding of the <code>AuthenticatedData</code> object just created,
257       *         wrapped into a ContentInfo
258       * 
259       * @throws CMSException if the <code>AuthenticatedData</code> object cannot
260       *                          be created
261       * @throws IOException if an I/O error occurs
262       */
263      public byte[] createAuthenticatedDataOutputStream(byte[] message,
264                                                        AlgorithmID macAlgorithm,
265                                                        int macKeyLength,
266                                                        AlgorithmID digestAlgorithm,
267                                                        int mode)
268        throws CMSException, IOException {
269        
270        AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
271        AlgorithmID digestAlg = null;
272        if (digestAlgorithm != null) {
273           digestAlg = (AlgorithmID)digestAlgorithm.clone();
274        }   
275        ObjectID contentType = ObjectID.cms_data;
276        
277        AuthenticatedDataOutputStream authenticatedData;
278    
279        //  a stream from which to read the data to be authenticated
280        ByteArrayInputStream is = new ByteArrayInputStream(message);
281        
282        // the stream to which to write the AuthenticatedData
283        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
284    
285        //  wrap AuthenticatedData into a ContentInfo 
286        ContentInfoOutputStream contentInfoStream = 
287          new ContentInfoOutputStream(ObjectID.cms_authData, resultStream);
288    
289        // create AuthenticatedDataOutputStream 
290        try {
291          authenticatedData = new AuthenticatedDataOutputStream(contentType,
292                                                                contentInfoStream, 
293                                                                macAlg,
294                                                                macKeyLength,
295                                                                null,
296                                                                digestAlg,
297                                                                mode);
298        } catch (NoSuchAlgorithmException ex) {
299          throw new CMSException(ex.toString());
300        }
301    
302        // static-static mode: set OriginatorInfo
303        OriginatorInfo originator = new OriginatorInfo();
304        originator.setCertificates(ecdhOriginatorCerts_);
305        authenticatedData.setOriginatorInfo(originator);
306        // create the recipient infos
307        RecipientInfo[] recipients = createRecipients();
308        // specify the recipients of the authenticated message
309        authenticatedData.setRecipientInfos(recipients);
310        
311        if (digestAlgorithm != null) {
312           // create some authenticated attributes
313           // (the message digest attribute is automatically added)
314           try {
315             Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
316             authenticatedData.setAuthenticatedAttributes(attributes);
317           } catch (Exception ex) {
318             throw new CMSException("Error creating attribute: " + ex.toString());   
319           } 
320        }    
321        
322        int blockSize = 20; // in real world we would use a block size like 2048
323        //  write in the data to be signed
324        byte[] buffer = new byte[blockSize];
325        int bytesRead;
326        while ((bytesRead = is.read(buffer)) != -1) {
327          authenticatedData.write(buffer, 0, bytesRead);
328        }
329        
330        // closing the stream adds auth/unauth attributes, calculates and adds the mac value, . 
331        authenticatedData.close();
332        return resultStream.toByteArray();
333      }
334      
335      
336      /**
337       * Decrypts the encrypted MAC key for the recipient identified by its index
338       * into the recipientInfos field and uses the MAC key to verify
339       * the authenticated data.
340       * <p>
341       * This way of decrypting the MAC key and verifying the content may be used for 
342       * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 
343       * KEKRecipientInfo, PasswordRecipeintInfo, OtherRecipientInfo), but requires to 
344       * know at what index of the recipientInfos field the RecipientInfo for the 
345       * particular recipient in mind can be found. 
346       * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
347       * some processing overhead may take place because a KeyAgreeRecipientInfo may
348       * contain encrypted mac keys for more than only one recipient; since the
349       * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
350       * mac key -- if there are more than only one -- repeated decryption runs may be
351       * required as long as the decryption process completes successfully.
352       *
353       * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
354       * @param message the content message, if transmitted by other means (explicit mode)
355       * @param key the key to decrypt the mac key 
356       * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
357       *                           which the given key belongs
358       *
359       * @return the verified message, as byte array
360       * 
361       * @throws CMSException if the authenticated data cannot be verified
362       * @throws IOException if a stream read/write error occurs
363       */
364      public byte[] getAuthenticatedDataStream(byte[] encoding, 
365                                               byte[] message, 
366                                               Key key, 
367                                               int recipientInfoIndex)
368        throws CMSException, IOException {
369    
370        // create the AuthenticatedData object from a DER encoded byte array
371        // we are testing the stream interface
372        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
373        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
374        
375        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
376          // in explicit mode explicitly supply the content for hash/mac computation  
377          authenticatedData.setInputStream(new ByteArrayInputStream(message));
378        }
379    
380        System.out.println("\nThis message can be verified by the following recipients:");
381        RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
382        
383        // for demonstration purposes we only look one time for all recipients included:
384        if (recipientInfoIndex == 0) {
385          int k = 0;
386          for (int i=0; i<recipients.length; i++) {
387            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
388            for (int j = 0; j < recipientIDs.length; j++) {
389              System.out.println("Recipient "+(++k)+":");
390              System.out.println(recipientIDs[j]);
391            }   
392          }
393        }
394        // decrypt the mac key and verify the mac for the indented recipient
395        try {
396          authenticatedData.setupMac(key, recipientInfoIndex);
397          InputStream contentStream = authenticatedData.getInputStream();
398          ByteArrayOutputStream os = new ByteArrayOutputStream();
399          Util.copyStream(contentStream, os, null);
400          
401          if (authenticatedData.verifyMac() == false) {
402            throw new CMSException("Mac verification error!");
403          }  
404          System.out.println("Mac successfully verified!");
405          
406          return os.toByteArray();
407    
408        } catch (InvalidKeyException ex) {
409          throw new CMSException("Key error: "+ex.getMessage());
410        } catch (NoSuchAlgorithmException ex) {
411          throw new CMSException(ex.toString());
412        }
413      }
414      
415      /**
416       * Decrypts the encrypted MAC key for the recipient identified by recipient identifier
417       * and uses the MAC key to verify the authenticated data.
418       * <p>
419       * This way of decrypting the mac key may be used for any type of RecipientInfo
420       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
421       * recipient in mind is identified by its recipient identifier.
422       *
423       * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
424       * @param message the content message, if transmitted by other means (explicit mode)
425       * @param key the key to decrypt the encrypted mac key
426       * @param recipientID the recipient identifier uniquely identifying the key of the
427       *        recipient
428       *
429       * @return the verified message, as byte array
430       * 
431       * @throws CMSException if the authenticated data cannot be verified
432       * @throws IOException if a stream read/write error occurs
433       */
434      public byte[] getAuthenticatedDataStream(byte[] encoding, 
435                                               byte[] message,
436                                               Key key, 
437                                               KeyIdentifier recipientID)
438        throws CMSException, IOException {
439    
440        // create the AuthenticatedData object from a BER encoded byte array
441        // we are testing the stream interface
442        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
443        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
444        
445        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
446          // in explicit mode explicitly supply the content for hash/mac computation  
447          authenticatedData.setInputStream(new ByteArrayInputStream(message));
448        }
449       
450        // get the right RecipientInfo
451        System.out.println("\nSearch for RecipientInfo:");
452        RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
453        if (recipient != null) {
454          System.out.println("RecipientInfo: " + recipient);   
455        } else {
456          throw new CMSException("No recipient with ID: " + recipientID);
457        }
458        // decrypt the mac key and verify the content mac
459        try {
460          System.out.println("Decrypt encrypted mac key...");
461          SecretKey cek = recipient.decryptKey(key, recipientID);
462          System.out.println("Verify content mac with decrypted mac key...");
463          authenticatedData.setupMac(cek);
464          InputStream contentStream = authenticatedData.getInputStream();
465          ByteArrayOutputStream os = new ByteArrayOutputStream();
466          Util.copyStream(contentStream, os, null);
467          
468          if (authenticatedData.verifyMac() == false) {
469            throw new CMSException("Mac verification error!");
470          } 
471          System.out.println("Mac successfully verified!");
472    
473          return os.toByteArray();
474    
475        } catch (InvalidKeyException ex) {
476          throw new CMSException("Key error: "+ex.getMessage());
477        } catch (NoSuchAlgorithmException ex) {
478          throw new CMSException(ex.toString());
479        }
480      }
481      
482      /**
483       * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
484       * the recipient identified by its recipient certificate.
485       * <p>
486       *
487       * @param encoding the <code>AuthenticatedData</code> object as DER encoded byte array
488       * @param message the content message, if transmitted by other means (explicit mode) 
489       * @param key the key to decrypt the message
490       * @param recipientCert the certificate of the recipient having a RecipientInfo of
491       *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
492       *
493       * @return the recovered message, as byte array
494       * @throws CMSException if the message cannot be recovered
495       * @throws IOException if a stream read/write error occurs
496       */
497      public byte[] getAuthenticatedDataStream(byte[] encoding, 
498                                               byte[] message,
499                                               Key key, 
500                                               X509Certificate recipientCert)
501        throws CMSException, IOException {
502    
503        // create the AuthenticatedData object from a DER encoded byte array
504        // we are testing the stream interface
505        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
506        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
507        
508        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
509          // in explicit mode explicitly supply the content for hash/mac computation  
510          authenticatedData.setInputStream(new ByteArrayInputStream(message));
511        }
512    
513       
514        // decrypt the mac key and verify the content mac
515        try {
516          System.out.println("Verify mac...");
517          authenticatedData.setupMac(key, recipientCert);
518          InputStream contentStream = authenticatedData.getInputStream();
519          ByteArrayOutputStream os = new ByteArrayOutputStream();
520          Util.copyStream(contentStream, os, null);
521          
522          if (authenticatedData.verifyMac() == false) {
523            throw new CMSException("Mac verification error!");
524          }
525          System.out.println("Mac successfully verified!");
526    
527          return os.toByteArray();
528    
529        } catch (InvalidKeyException ex) {
530          throw new CMSException("Key error: "+ex.getMessage());
531        } catch (NoSuchAlgorithmException ex) {
532          throw new CMSException(ex.toString());
533        }
534      }
535    
536      
537      // non stream
538    
539      /**
540       * Creates a CMS <code>AuthenticatedData</code> for the given message message.
541       *
542       * @param message the message to be authenticated, as byte representation
543       * @param macAlgorithm the mac algorithm to be used
544       * @param macKeyLength the length of the temporary MAC key to be generated
545       * @param digestAlgorithm the digest algorithm to be used to calculate a digest
546       *                        from the content if authenticated attributes should
547       *                        be included
548       * @param mode whether to include the content into the AuthenticatedData ({@link
549       *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
550       *             ({@link AuthenticatedDataStream#EXPLICIT explicit}) 
551       *                                    
552       * @return the BER encoding of the <code>AuthenticatedData</code> object just created
553       * 
554       * @throws CMSException if the <code>AuthenticatedData</code> object cannot
555       *                          be created
556       */
557      public byte[] createAuthenticatedData(byte[] message,
558                                            AlgorithmID macAlgorithm,
559                                            int macKeyLength,
560                                            AlgorithmID digestAlgorithm,
561                                            int mode)
562        throws CMSException {
563            
564        AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
565        AlgorithmID digestAlg = null;
566        if (digestAlgorithm != null) {
567           digestAlg = (AlgorithmID)digestAlgorithm.clone();
568        }   
569        ObjectID contentType = ObjectID.cms_data;
570        
571        AuthenticatedData authenticatedData;
572    
573        // create a new AuthenticatedData object 
574        try {
575          authenticatedData = new AuthenticatedData(contentType,
576                                                    message, 
577                                                    macAlg,
578                                                    macKeyLength,
579                                                    null,
580                                                    digestAlg,
581                                                    mode);
582        } catch (NoSuchAlgorithmException ex) {
583          throw new CMSException(ex.toString());
584        }
585    
586        // static-static mode: set OriginatorInfo
587        OriginatorInfo originator = new OriginatorInfo();
588        originator.setCertificates(ecdhOriginatorCerts_);
589        authenticatedData.setOriginatorInfo(originator);
590        // create the recipient infos
591        RecipientInfo[] recipients = createRecipients();
592        // specify the recipients of the authenticated message
593        authenticatedData.setRecipientInfos(recipients);
594        
595        if (digestAlgorithm != null) {
596           // create some authenticated attributes
597           // (the message digest attribute is automatically added)
598           try {
599             Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
600             authenticatedData.setAuthenticatedAttributes(attributes);
601           } catch (Exception ex) {
602             throw new CMSException("Error creating attribute: " + ex.toString());   
603           } 
604        }    
605       
606        // wrap into ContentInfo
607        ContentInfo contentInfo = new ContentInfo(authenticatedData);
608        // return encoded EnvelopedData
609        return contentInfo.getEncoded();
610      
611      }
612    
613      /**
614       * Decrypts the encrypted MAC key for the recipient identified by its index
615       * into the recipientInfos field and uses the MAC key to verify
616       * the authenticated data.
617       * <p>
618       * This way of decrypting the MAC key and verifying the content may be used for 
619       * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 
620       * KEKRecipientInfo), but requires to know at what index of the recipientInfos
621       * field the RecipientInfo for the particular recipient in mind can be found. 
622       * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
623       * some processing overhead may take place because a KeyAgreeRecipientInfo may
624       * contain encrypted mac keys for more than only one recipient; since the
625       * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
626       * mac key -- if there are more than only one -- repeated decryption runs may be
627       * required as long as the decryption process completes successfully.
628       *
629       * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
630       * @param message the content message, if transmitted by other means (explicit mode)
631       * @param key the key to decrypt the mac key
632       * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
633       *                           which the given key belongs
634       *
635       * @return the verified message, as byte array
636       * @throws CMSException if the authenticated data cannot be verified
637       * @throws IOException if a IO read/write error occurs
638       */
639      public byte[] getAuthenticatedData(byte[] encoding, 
640                                         byte[] message,
641                                         Key key,
642                                         int recipientInfoIndex) 
643        throws CMSException, IOException {
644            
645        // create the AuthenticatedData object from a DER encoded byte array
646        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
647        AuthenticatedData authenticatedData = new AuthenticatedData(is);
648        
649        if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
650          // in explicit mode explicitly supply the content for hash/mac computation  
651          authenticatedData.setContent(message);
652        }
653    
654        System.out.println("\nThis message can be verified by the owners of the following recipients:");
655        RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
656        
657        // for demonstration purposes we only look one time for all recipients included:
658        if (recipientInfoIndex == 0) {
659          int k = 0;
660          for (int i=0; i<recipients.length; i++) {
661            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
662            for (int j = 0; j < recipientIDs.length; j++) {
663              System.out.println("Recipient "+(++k)+":");
664              System.out.println(recipientIDs[j]);
665            }   
666          }
667        }
668        // decrypt the mac key and verify the mac for the first recipient
669        try {
670          authenticatedData.setupMac(key, recipientInfoIndex);
671          if (authenticatedData.verifyMac() == false) {
672            throw new CMSException("Mac verification error!");
673          }  
674          System.out.println("Mac successfully verified!");
675          
676          return authenticatedData.getContent();
677    
678        } catch (InvalidKeyException ex) {
679          throw new CMSException("Key error: "+ex.getMessage());
680        } catch (NoSuchAlgorithmException ex) {
681          throw new CMSException(ex.toString());
682        }
683      }
684      
685      /**
686       * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
687       * the recipient identified by recipient identifier.
688       * <p>
689       * This way of decrypting the content may be used for any type of RecipientInfo
690       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
691       * recipient in mind is identified by its recipient identifier.
692       *
693       * @param encoding the DER encoeded <code>AuthenticatedData</code> object#
694       * @param message the content message, if transmitted by other means (explicit mode)
695       * @param key the key to decrypt the message
696       * @param recipientID the recipient identifier uniquely identifying the key of the
697       *        recipient
698       *
699       * @return the recovered message, as byte array
700       * @throws CMSException if the message cannot be recovered
701       * @throws IOException if an I/O error occurs
702       */
703      public byte[] getAuthenticatedData(byte[] encoding, 
704                                         byte[] message,
705                                         Key key, 
706                                         KeyIdentifier recipientID) 
707        throws CMSException, IOException {
708            
709        // create the AuthenticatedData object from a DER encoded byte array
710        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
711        AuthenticatedData authenticatedData = new AuthenticatedData(is);
712        
713        if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
714          // in explicit mode explicitly supply the content for hash/mac computation  
715          authenticatedData.setContent(message);
716        }
717       
718        // get the right RecipientInfo
719        System.out.println("\nSearch for RecipientInfo:");
720        RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
721        if (recipient != null) {
722          System.out.println("RecipientInfo: " + recipient);   
723        } else {
724          throw new CMSException("No recipient with ID " + recipientID);
725        }
726        // decrypt the mac key and verify the content mac
727        try {
728          System.out.println("Decrypt encrypted mac key...");
729          SecretKey cek = recipient.decryptKey(key, recipientID);
730          System.out.println("Verify content mac with decrypted mac key...");
731          authenticatedData.setupMac(cek);
732          
733          if (authenticatedData.verifyMac() == false) {
734            throw new CMSException("Mac verification error!");
735          } 
736          System.out.println("Mac successfully verified!");
737    
738          return authenticatedData.getContent();
739    
740        } catch (InvalidKeyException ex) {
741          throw new CMSException("Key error: "+ex.getMessage());
742        } 
743      }
744      
745      /**
746       * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
747       * the recipient identified by its recipient certificate.
748       * <p>
749       *
750       * @param encoding the DER encoded <code>AuthenticatedData</code> ASN.1 object
751       * @param message the content message, if transmitted by other means (explicit mode) 
752       * @param key the key to decrypt the message
753       * @param recipientCert the certificate of the recipient having a RecipientInfo of
754       *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
755       *
756       * @return the recovered message, as byte array
757       * @throws CMSException if the message cannot be recovered
758       */
759      public byte[] getAuthenticatedData(byte[] encoding, 
760                                         byte[] message,
761                                         Key key,
762                                         X509Certificate recipientCert) 
763        throws CMSException, IOException {
764    
765        // create the AuthenticatedData object from a DER encoded byte array
766        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
767        AuthenticatedData authenticatedData = new AuthenticatedData(is);
768        
769        if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
770          // in explicit mode explicitly supply the content for hash/mac computation  
771          authenticatedData.setContent(message);
772        }
773    
774        // decrypt the mac key and verify the content mac
775        try {
776          System.out.println("Verify mac...");
777          authenticatedData.setupMac(key, recipientCert);
778          if (authenticatedData.verifyMac() == false) {
779            throw new CMSException("Mac verification error!");
780          }
781          System.out.println("Mac successfully verified!");
782    
783          return authenticatedData.getContent();
784          
785        } catch (InvalidKeyException ex) {
786          throw new CMSException("Key error: "+ex.getMessage());
787        } catch (NoSuchAlgorithmException ex) {
788          throw new CMSException(ex.toString());
789        } 
790      }
791      
792      /**
793       * Parses an AuthenticatedData and verifies the mac for all recipients
794       * using the index into the recipientInfos field for identifying the recipient.
795       *
796       * @param stream whether to use AuthenticatedDataStream or AuthenticatedData
797       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
798       * @param message the content message, if transmitted by other means (explicit mode) 
799       *
800       * @throws Exception if some error occurs during decoding/decryption
801       */ 
802      public void parseAuthenticatedDataWithRecipientInfoIndex(boolean stream,
803        byte[] encodedAuthenticatedData, byte[] message) throws Exception {
804        byte[] receivedMessage;
805        if (stream) {
806          // ecdhUser1
807          System.out.println("\nDecrypt for ecdhUser1:");
808          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser1Pk_, 0);
809          System.out.print("\nDecrypted content: ");
810          System.out.println(new String(receivedMessage));
811          // ecdhUser2
812          System.out.println("\nDecrypt for ecdhUser2:");
813          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser2Pk_, 0);
814          System.out.print("\nDecrypted content: ");
815          System.out.println(new String(receivedMessage));
816        } else {
817          // ecdhUser1
818          System.out.println("\nDecrypt for ecdhUser1:");
819          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser1Pk_, 0);
820          System.out.print("\nDecrypted content: ");
821          System.out.println(new String(receivedMessage));
822          // ecdhUser2
823          System.out.println("\nDecrypt for ecdhUser2:");
824          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser2Pk_, 0);
825          System.out.print("\nDecrypted content: ");
826          System.out.println(new String(receivedMessage));
827        }    
828      }
829      
830      /**
831       * Parses an AuthenticatedData and verifies the mac for all recipients
832       * using their recipient identifiers for identifying the recipient.
833       *
834       * @param stream whether to use AuthenticatedDataStream or AuthenticatedData
835       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
836       * @param message the content message, if transmitted by other means (explicit mode)
837       *
838       * @throws Exception if some error occurs during decoding/decryption
839       */ 
840      public void parseAuthenticatedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthenticatedData,
841        byte[] message) throws Exception {
842        byte[] receivedMessage;
843        if (stream) {
844          // ecdhUser1
845          System.out.println("\nDecrypt for ecdhUser1:");
846          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
847          System.out.print("\nDecrypted content: ");
848          System.out.println(new String(receivedMessage));
849          // ecdhUser2
850          System.out.println("\nDecrypt for ecdhUser2:");
851          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
852          System.out.print("\nDecrypted content: ");
853          System.out.println(new String(receivedMessage));
854        } else {
855          // ecdhUser1
856          System.out.println("\nDecrypt for ecdhUser1:");
857          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
858          System.out.print("\nDecrypted content: ");
859          System.out.println(new String(receivedMessage));
860          // ecdhUser2
861          System.out.println("\nDecrypt for ecdhUser2:");
862          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
863          System.out.print("\nDecrypted content: ");
864          System.out.println(new String(receivedMessage));
865        }    
866      }
867      
868      /**
869       * Parses an AuthenticatedData and verifies the mac for all recipients
870       * using the recipient certificate for identifying the recipient.
871       *
872       * @param stream whether to use AuthenticatedDataStream or AuthenticatedData
873       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
874       * @param message the content message, if transmitted by other means (explicit mode)
875       *
876       * @throws Exception if some error occurs during decoding/decryption
877       */ 
878      public void parseAuthenticatedDataWithRecipientCertificate(boolean stream, byte[] encodedAuthenticatedData,
879        byte[] message) throws Exception {
880        byte[] receivedMessage;
881        if (stream) {
882          // ecdhUser1
883          System.out.println("\nDecrypt for ecdhUser1:");
884          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser1Pk_, ecdhUser1_);
885          System.out.print("\nDecrypted content: ");
886          System.out.println(new String(receivedMessage));
887          // ecdhUser2
888          System.out.println("\nDecrypt for ecdhUser2:");
889          receivedMessage = getAuthenticatedDataStream(encodedAuthenticatedData, message, ecdhUser2Pk_, ecdhUser2_);
890          System.out.print("\nDecrypted content: ");
891          System.out.println(new String(receivedMessage));
892        } else {
893          // ecdhUser1
894          System.out.println("\nDecrypt for ecdhUser1:");
895          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser1Pk_, ecdhUser1_);
896          System.out.print("\nDecrypted content: ");
897          System.out.println(new String(receivedMessage));
898          // ecdhUser2
899          System.out.println("\nDecrypt for ecdhUser2:");
900          receivedMessage = getAuthenticatedData(encodedAuthenticatedData, message, ecdhUser2Pk_, ecdhUser2_);
901          System.out.print("\nDecrypted content: ");
902          System.out.println(new String(receivedMessage));
903        }    
904      }
905      
906      
907      
908      /**
909       * Starts the demo.
910       */
911      public void start() {
912         // the test message
913        String m = "This is the test message.";
914        System.out.println("Test message: \""+m+"\"");
915        System.out.println();
916        byte[] message = m.getBytes();
917        
918        AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
919        int macKeyLength = 32;
920        AlgorithmID digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
921    
922        try {
923          byte[] encoding;
924          System.out.println("Stream implementation demos");
925          System.out.println("===========================");
926    
927    
928          // the stream implementation
929          //
930          // test CMS AuthenticatedDataStream
931          //
932    
933          int[] modes = { AuthenticatedDataStream.IMPLICIT, AuthenticatedDataStream.EXPLICIT };
934          
935          for (int i = 0; i < modes.length; i++) {
936            int mode = modes[i];
937            if (mode == AuthenticatedDataStream.IMPLICIT) {
938              System.out.print("Implicit ");
939            } else {
940              System.out.print("Explicit ");
941            }
942            // with authenticated attributes
943            System.out.println("AuthenticatedDataStream demo with authenticated attributes [create]:\n");
944            encoding =  createAuthenticatedDataStream(message,
945                                                      macAlgorithm,
946                                                      macKeyLength,
947                                                      digestAlgorithm,
948                                                      mode);
949            // transmit data
950            System.out.println("\nAuthenticatedDataStream demo with authenticated attributes [parse]:\n");
951            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
952            parseAuthenticatedDataWithRecipientInfoIndex(true, encoding, message);
953            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
954            parseAuthenticatedDataWithRecipientIdentifier(true, encoding, message);
955            System.out.println("Parse for the several recipients using their certificate.");
956            parseAuthenticatedDataWithRecipientCertificate(true, encoding, message);
957            
958            // without authenticated attributes
959            System.out.println("AuthenticatedDataStream demo without authenticated attributes [create]:\n");
960            encoding =  createAuthenticatedDataStream(message,
961                                                      macAlgorithm,
962                                                      macKeyLength,
963                                                      null,
964                                                      mode);
965            // transmit data
966            System.out.println("\nAuthenticatedDataStream demo without authenticated attributes [parse]:\n");
967            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
968            parseAuthenticatedDataWithRecipientInfoIndex(true, encoding, message);
969            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
970            parseAuthenticatedDataWithRecipientIdentifier(true, encoding, message);
971            System.out.println("Parse for the several recipients using their certificate.");
972            parseAuthenticatedDataWithRecipientCertificate(true, encoding, message);
973         
974          }
975          
976          System.out.println("\nOutputStream implementation demos");
977          System.out.println("=================================");
978    
979    
980          // the output stream implementation
981          //
982          // test CMS AuthenticatedDataOutputStream
983          //
984          for (int i = 0; i < modes.length; i++) {
985            int mode = modes[i];
986            if (mode == AuthenticatedDataStream.IMPLICIT) {
987              System.out.print("Implicit ");
988            } else {
989              System.out.print("Explicit ");
990            }
991            // with authenticated attributes
992            System.out.println("AuthenticatedDataOutputStream demo with authenticated attributes [create]:\n");
993            encoding =  createAuthenticatedDataOutputStream(message,
994                                                            macAlgorithm,
995                                                            macKeyLength,
996                                                            digestAlgorithm,
997                                                            mode);
998            // transmit data
999            System.out.println("\nAuthenticatedDataStream demo with authenticated attributes [parse]:\n");
1000            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
1001            parseAuthenticatedDataWithRecipientInfoIndex(true, encoding, message);
1002            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
1003            parseAuthenticatedDataWithRecipientIdentifier(true, encoding, message);
1004            System.out.println("Parse for the several recipients using their certificate.");
1005            parseAuthenticatedDataWithRecipientCertificate(true, encoding, message);
1006            
1007            // without authenticated attributes
1008            System.out.println("AuthenticatedDataOutputStream demo without authenticated attributes [create]:\n");
1009            encoding =  createAuthenticatedDataOutputStream(message,
1010                                                      macAlgorithm,
1011                                                      macKeyLength,
1012                                                      null,
1013                                                      mode);
1014            // transmit data
1015            System.out.println("\nAuthenticatedDataOutputStream demo without authenticated attributes [parse]:\n");
1016            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
1017            parseAuthenticatedDataWithRecipientInfoIndex(true, encoding, message);
1018            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
1019            parseAuthenticatedDataWithRecipientIdentifier(true, encoding, message);
1020            System.out.println("Parse for the several recipients using their certificate.");
1021            parseAuthenticatedDataWithRecipientCertificate(true, encoding, message);
1022         
1023          }
1024          System.out.println("===============================");
1025    
1026                
1027          //
1028          // test CMS AuthenticatedData
1029          //
1030          for (int i = 0; i < modes.length; i++) {
1031            int mode = modes[i];
1032            if (mode == AuthenticatedDataStream.IMPLICIT) {
1033              System.out.print("Implicit ");
1034            } else {
1035              System.out.print("Explicit ");
1036            }
1037            // with authenticated attributes
1038            System.out.println("AuthenticatedData demo with authenticated attributes [create]:\n");
1039            encoding =  createAuthenticatedData(message,
1040                                               macAlgorithm,
1041                                               macKeyLength,
1042                                               digestAlgorithm,
1043                                               mode);
1044            // transmit data
1045            System.out.println("\nAuthenticatedDataStream demo with authenticated attributes [parse]:\n");
1046            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
1047            parseAuthenticatedDataWithRecipientInfoIndex(false, encoding, message);
1048            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
1049            parseAuthenticatedDataWithRecipientIdentifier(false, encoding, message);
1050            System.out.println("Parse for the several recipients using their certificate.");
1051            parseAuthenticatedDataWithRecipientCertificate(false, encoding, message);
1052            
1053            // without authenticated attributes
1054            System.out.println("AuthenticatedData demo without authenticated attributes [create]:\n");
1055            encoding =  createAuthenticatedData(message,
1056                                                macAlgorithm,
1057                                                macKeyLength,
1058                                                null,
1059                                                mode);
1060            // transmit data
1061            System.out.println("\nAuthenticatedData demo without authenticated attributes [parse]:\n");
1062            System.out.println("Parse for the several recipients using their index into the recipientInfos field.");
1063            parseAuthenticatedDataWithRecipientInfoIndex(false, encoding, message);
1064            System.out.println("Parse for the several recipients using their RecipientIdentifier.");
1065            parseAuthenticatedDataWithRecipientIdentifier(false, encoding, message);
1066            System.out.println("Parse for the several recipients using their certificate.");
1067            parseAuthenticatedDataWithRecipientCertificate(false, encoding, message);
1068         
1069          }
1070          
1071          
1072    
1073        } catch (Exception ex) {
1074          ex.printStackTrace();
1075          throw new RuntimeException(ex.toString());
1076        }
1077      }
1078      
1079      /**
1080       * Main method.
1081       *
1082       * @throws IOException
1083       *            if an I/O error occurs when reading required keys
1084       *            and certificates from the keystore file
1085       */
1086      public static void main(String argv[]) throws Exception {
1087    
1088        DemoUtil.initDemos();
1089        ECCDemoUtil.installIaikEccProvider();
1090        (new ECDHAuthenticatedDataDemo()).start();
1091        System.out.println("\nReady!");
1092        System.in.read();
1093      }
1094    }