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/authenticatedData/AuthenticatedDataOutputStreamDemo.java 19    12.02.25 17:58 Dbratko $
059    // $Revision: 19 $demo.cms.authenticatedData
060    package demo.cms.authenticatedData;
061    
062    import iaik.asn1.ObjectID;
063    import iaik.asn1.structures.AlgorithmID;
064    import iaik.asn1.structures.Attribute;
065    import iaik.cms.AuthenticatedDataOutputStream;
066    import iaik.cms.AuthenticatedDataStream;
067    import iaik.cms.CMSAlgorithmID;
068    import iaik.cms.CMSException;
069    import iaik.cms.CertificateIdentifier;
070    import iaik.cms.ContentInfoOutputStream;
071    import iaik.cms.IssuerAndSerialNumber;
072    import iaik.cms.KEKIdentifier;
073    import iaik.cms.KEKRecipientInfo;
074    import iaik.cms.KeyAgreeRecipientInfo;
075    import iaik.cms.KeyIdentifier;
076    import iaik.cms.KeyTransRecipientInfo;
077    import iaik.cms.OriginatorInfo;
078    import iaik.cms.RecipientInfo;
079    import iaik.cms.RecipientKeyIdentifier;
080    import iaik.cms.SecurityProvider;
081    import iaik.cms.SubjectKeyID;
082    import iaik.cms.attributes.CMSContentType;
083    import iaik.security.random.SecRandom;
084    import iaik.utils.Util;
085    import iaik.x509.X509Certificate;
086    
087    import java.io.ByteArrayInputStream;
088    import java.io.ByteArrayOutputStream;
089    import java.io.IOException;
090    import java.io.InputStream;
091    import java.security.InvalidKeyException;
092    import java.security.Key;
093    import java.security.NoSuchAlgorithmException;
094    import java.security.PrivateKey;
095    import java.security.SecureRandom;
096    
097    import javax.crypto.KeyGenerator;
098    import javax.crypto.SecretKey;
099    
100    import demo.DemoUtil;
101    import demo.keystore.CMSKeyStore;
102    
103    /**
104     * Demonstrates the usage of class {@link iaik.cms.AuthenticatedDataOutputStream} and
105     * {@link iaik.cms.AuthenticatedDataOutputStream} for recipient-specific protecting the 
106     * integrity of message using the CMS type AuthenticatedData.
107     * <p>
108     * <b>Attention:</b> This demo uses Static-Static Diffie-Hellman as key management 
109     * technique for providing origin authentication. 
110     * <p>
111     * This demo requires that you have <code>iaik_esdh.jar</code>
112     * (or <code>iaik_jce_full.jar</code>) in your classpath.
113     * You can download it from <a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
114     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>.
115     * 
116     *
117     * @see iaik.cms.AuthenticatedDataStream
118     * @see iaik.cms.AuthenticatedDataOutputStream
119     * 
120     */
121    public class AuthenticatedDataOutputStreamDemo {
122      
123      // certificate of rsaUser 1
124      X509Certificate rsaUser1;
125      // private key of rsaUser 1
126      PrivateKey rsaUser1_pk;
127      // certificate of rsaUser 2
128      X509Certificate rsaUser2;
129      // private key of rsaUser 2
130      PrivateKey rsaUser2_pk;
131    
132      // certificate of (originator) SSDH User 1 (static-static Diffie-Hellman)
133      X509Certificate ssdhUser1;
134      X509Certificate[] originatorCerts;
135      // private key of SSDH User 1
136      PrivateKey ssdhUser1_pk;
137      // certificate of SSDH User 2 (static-static Diffie-Hellman)
138      X509Certificate ssdhUser2;
139      // private key of SSDH User 2
140      PrivateKey ssdhUser2_pk;
141      
142      // key encryption key for KEKRecipientInfo
143      SecretKey kek;
144      byte[] kekID;
145    
146      
147      // secure random number generator
148      SecureRandom random;
149    
150      /**
151       * Setup the demo certificate chains.
152       *
153       * Keys and certificate are retrieved from the demo KeyStore which
154       * has to be located in your current working directory and may be
155       * created by running {@link demo.keystore.SetupCMSKeyStore
156       * SetupCMSKeyStore}.
157       *
158       * @throws IOException if an file read error occurs
159       */
160      public AuthenticatedDataOutputStreamDemo() throws IOException {
161        
162        System.out.println();
163        System.out.println("**********************************************************************************");
164        System.out.println("*                        AuthenticatedDataOutputStream demo                      *");
165        System.out.println("*   (shows the usage of the CMS AuthenticatedDataOutputStream implementation)    *");
166        System.out.println("**********************************************************************************");
167        System.out.println();
168        
169        // add all certificates to the list
170        rsaUser1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
171        rsaUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
172        rsaUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
173        rsaUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
174        
175        originatorCerts = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1);
176        ssdhUser1 = originatorCerts[0];
177        ssdhUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1);
178        ssdhUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2)[0];
179        ssdhUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2);
180        
181        random = SecRandom.getDefault();
182        
183        //  create a secret key encryption key for a KEKRecipientInfo
184        KeyGenerator kg;
185        try {
186          kg = SecurityProvider.getSecurityProvider().getKeyGenerator("3DES", 192);
187        } catch (NoSuchAlgorithmException ex) {
188          throw new IOException("KeyGenerator for 3DES not avcailable!");   
189        }   
190        kek = kg.generateKey();
191        kekID = new byte[] { 00, 00, 00, 01 };
192        
193      }
194    
195    
196      /**
197       * Creates a CMS <code>AuthenticatedDataOutputStream</code> for the given message message.
198       *
199       * @param message the message to be authenticated, as byte representation
200       * @param macAlgorithm the mac algorithm to be used
201       * @param macKeyLength the length of the temporary MAC key to be generated
202       * @param digestAlgorithm the digest algorithm to be used to calculate a digest
203       *                        from the content if authenticated attributes should
204       *                        be included
205       * @param mode whether to include the content into the AuthenticatedData ({@link
206       *             AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
207       *             ({@link AuthenticatedDataStream#EXPLICIT explicit})
208       *                                     
209       * @return the BER encoding of the <code>AuthenticatedData</code> object just created,
210       *         wrapped into a ContentInfo
211       * 
212       * @throws CMSException if the <code>AuthenticatedData</code> object cannot
213       *                          be created
214       * @throws IOException if an I/O error occurs
215       */
216      public byte[] createAuthenticatedDataStream(byte[] message,
217                                                  AlgorithmID macAlgorithm,
218                                                  int macKeyLength,
219                                                  AlgorithmID digestAlgorithm,
220                                                  int mode)
221        throws CMSException, IOException {
222        
223        AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
224        AlgorithmID digestAlg = null;
225        if (digestAlgorithm != null) {
226           digestAlg = (AlgorithmID)digestAlgorithm.clone();
227        }   
228        ObjectID contentType = ObjectID.cms_data;
229        
230        AuthenticatedDataOutputStream authenticatedData;
231    
232        //  a stream from which to read the data to be authenticated
233        ByteArrayInputStream is = new ByteArrayInputStream(message);
234        
235        // the stream to which to write the AuthenticatedData
236        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
237    
238        //  wrap AuthenticatedData into a ContentInfo 
239        ContentInfoOutputStream contentInfoStream = 
240          new ContentInfoOutputStream(ObjectID.cms_authData, resultStream);
241    
242        // create AuthenticatedDataOutputStream 
243        try {
244          authenticatedData = new AuthenticatedDataOutputStream(contentType,
245                                                                contentInfoStream, 
246                                                                macAlg,
247                                                                macKeyLength,
248                                                                null,
249                                                                digestAlg,
250                                                                mode);
251        } catch (NoSuchAlgorithmException ex) {
252          throw new CMSException(ex.toString());
253        }
254    
255        // static-static mode: set OriginatorInfo
256        OriginatorInfo originator = new OriginatorInfo();
257        originator.setCertificates(originatorCerts);
258        authenticatedData.setOriginatorInfo(originator);
259        // create the recipient infos
260        RecipientInfo[] recipients = createRecipients();
261        // specify the recipients of the authenticated message
262        authenticatedData.setRecipientInfos(recipients);
263        
264        if (digestAlgorithm != null) {
265           // create some authenticated attributes
266           // (the message digest attribute is automatically added)
267           try {
268             Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
269             authenticatedData.setAuthenticatedAttributes(attributes);
270           } catch (Exception ex) {
271             throw new CMSException("Error creating attribute: " + ex.toString());   
272           } 
273        }    
274        
275        int blockSize = 20; // in real world we would use a block size like 2048
276        //  write in the data to be signed
277        byte[] buffer = new byte[blockSize];
278        int bytesRead;
279        while ((bytesRead = is.read(buffer)) != -1) {
280          authenticatedData.write(buffer, 0, bytesRead);
281        }
282        
283        // closing the stream adds auth/unauth attributes, calculates and adds the mac value, . 
284        authenticatedData.close();
285        return resultStream.toByteArray();
286      }
287    
288      /**
289       * Decrypts the encrypted MAC key for the recipient identified by its index
290       * into the recipientInfos field and uses the MAC key to verify
291       * the authenticated data.
292       * <p>
293       * This way of decrypting the MAC key and verifying the content may be used for 
294       * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 
295       * KEKRecipientInfo, PasswordRecipeintInfo, OtherRecipientInfo), but requires to 
296       * know at what index of the recipientInfos field the RecipientInfo for the 
297       * particular recipient in mind can be found. 
298       * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
299       * some processing overhead may take place because a KeyAgreeRecipientInfo may
300       * contain encrypted mac keys for more than only one recipient; since the
301       * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
302       * mac key -- if there are more than only one -- repeated decryption runs may be
303       * required as long as the decryption process completes successfully.
304       *
305       * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
306       * @param message the content message, if transmitted by other means (explicit mode)
307       * @param key the key to decrypt the mac key 
308       * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
309       *                           which the given key belongs
310       *
311       * @return the verified message, as byte array
312       * 
313       * @throws CMSException if the authenticated data cannot be verified
314       * @throws IOException if a stream read/write error occurs
315       */
316      public byte[] getAuthenticatedDataStream(byte[] encoding, 
317                                               byte[] message, 
318                                               Key key, 
319                                               int recipientInfoIndex)
320        throws CMSException, IOException {
321    
322        // parse the BER encoded AuthenticatedData
323        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
324        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
325        
326        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
327          // in explicit mode explicitly supply the content for hash/mac computation  
328          authenticatedData.setInputStream(new ByteArrayInputStream(message));
329        }
330    
331        System.out.println("\nThis message can be verified by the following recipients:");
332        RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
333        
334        // for demonstration purposes we only look one time for all recipients included:
335        if (recipientInfoIndex == 0) {
336          int k = 0;
337          for (int i=0; i<recipients.length; i++) {
338            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
339            for (int j = 0; j < recipientIDs.length; j++) {
340              System.out.println("Recipient "+(++k)+":");
341              System.out.println(recipientIDs[j]);
342            }   
343          }
344        }
345        // decrypt the mac key and verify the mac for the indented recipient
346        try {
347          authenticatedData.setupMac(key, recipientInfoIndex);
348          InputStream contentStream = authenticatedData.getInputStream();
349          ByteArrayOutputStream os = new ByteArrayOutputStream();
350          Util.copyStream(contentStream, os, null);
351          
352          if (authenticatedData.verifyMac() == false) {
353            throw new CMSException("Mac verification error!");
354          }  
355          System.out.println("Mac successfully verified!");
356          
357          return os.toByteArray();
358    
359        } catch (InvalidKeyException ex) {
360          throw new CMSException("Key error: "+ex.getMessage());
361        } catch (NoSuchAlgorithmException ex) {
362          throw new CMSException(ex.toString());
363        }
364      }
365      
366      /**
367       * Decrypts the encrypted MAC key for the recipient identified by recipient identifier
368       * and uses the MAC key to verify the authenticated data.
369       * <p>
370       * This way of decrypting the encrypted mac key may be used for any type of RecipientInfo
371       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
372       * recipient in mind is identified by its recipient identifier.
373       *
374       * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
375       * @param key the key to decrypt the encrypted mac key
376       * @param recipientID the recipient identifier uniquely identifying the key of the
377       *        recipient
378       *
379       * @return the verified message, as byte array
380       * 
381       * @throws CMSException if the authenticated data cannot be verified
382       * @throws IOException if a stream read/write error occurs
383       */
384      public byte[] getAuthenticatedDataStream(byte[] encoding, 
385                                               byte[] message,
386                                               Key key, 
387                                               KeyIdentifier recipientID)
388        throws CMSException, IOException {
389    
390        // parse the BER encoded AuthenticatedData 
391        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
392        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
393        
394        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
395          // in explicit mode explicitly supply the content for hash/mac computation  
396          authenticatedData.setInputStream(new ByteArrayInputStream(message));
397        }
398       
399        // get the right RecipientInfo
400        System.out.println("\nSearch for RecipientInfo:");
401        RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
402        if (recipient != null) {
403          System.out.println("RecipientInfo: " + recipient);   
404        } else {
405          throw new CMSException("No recipient with ID: " + recipientID);
406        }    
407        // decrypt the mac key and verify the content mac
408        try {
409          System.out.println("Decrypt encrypted mac key...");
410          SecretKey cek = recipient.decryptKey(key, recipientID);
411          System.out.println("Verify content mac with decrypted mac key...");
412          authenticatedData.setupMac(cek);
413          InputStream contentStream = authenticatedData.getInputStream();
414          ByteArrayOutputStream os = new ByteArrayOutputStream();
415          Util.copyStream(contentStream, os, null);
416          
417          if (authenticatedData.verifyMac() == false) {
418            throw new CMSException("Mac verification error!");
419          } 
420          System.out.println("Mac successfully verified!");
421    
422          return os.toByteArray();
423    
424        } catch (InvalidKeyException ex) {
425          throw new CMSException("Key error: "+ex.getMessage());
426        } catch (NoSuchAlgorithmException ex) {
427          throw new CMSException(ex.toString());
428        }
429      }
430      
431      /**
432       * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
433       * the recipient identified by its recipient certificate or kekID.
434       * <p>
435       *
436       * @param encoding the <code>AuthenticatedData</code> object as DER encoded byte array
437       * @param key the key to decrypt the message
438       * @param recipientCert the certificate of the recipient having a RecipientInfo of
439       *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
440       * @param kekID the kekID identifying the recipient key when using a RecipientInfo
441       *              of type KEKRecipientInfo                     
442       *
443       * @return the verified message, as byte array
444       * 
445       * @throws CMSException if the authenticated data cannot be verified
446       * @throws IOException if a stream read/write error occurs
447       */
448      public byte[] getAuthenticatedDataStream(byte[] encoding, 
449                                               byte[] message,
450                                               Key key, 
451                                               X509Certificate recipientCert,
452                                               byte[] kekID)
453        throws CMSException, IOException {
454    
455        // create the AuthenticatedData object from a DER encoded byte array
456        // we are testing the stream interface
457        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
458        AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
459        
460        if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
461          // in explicit mode explicitly supply the content for hash/mac computation  
462          authenticatedData.setInputStream(new ByteArrayInputStream(message));
463        }
464    
465       
466        // decrypt the mac key and verify the content mac
467        try {
468          System.out.println("Verify mac...");
469          if (recipientCert != null) {
470            authenticatedData.setupMac(key, recipientCert);
471          } else {
472            // KEKRecipientInfo
473            authenticatedData.setupMac(key, new KEKIdentifier(kekID));
474          }
475          InputStream contentStream = authenticatedData.getInputStream();
476          ByteArrayOutputStream os = new ByteArrayOutputStream();
477          Util.copyStream(contentStream, os, null);
478          
479          if (authenticatedData.verifyMac() == false) {
480            throw new CMSException("Mac verification error!");
481          }
482          System.out.println("Mac successfully verified!");
483    
484          return os.toByteArray();
485    
486        } catch (InvalidKeyException ex) {
487          throw new CMSException("Key error: "+ex.getMessage());
488        } catch (NoSuchAlgorithmException ex) {
489          throw new CMSException(ex.toString());
490        }
491      }
492    
493      
494      /**
495       * Creates the RecipientInfos.
496       *
497       * @return the RecipientInfos created
498       *
499       * @throws CMSException if an error occurs when creating the recipient infos
500       */
501      public RecipientInfo[] createRecipients() throws CMSException {
502        
503        
504        RecipientInfo[] recipients = new RecipientInfo[4];
505        try {
506          
507          // rsaUser1 is the first receiver (cert identified by IssuerAndSerialNumber)
508          recipients[0] = new KeyTransRecipientInfo(rsaUser1, 
509                                                    (AlgorithmID)AlgorithmID.rsaEncryption.clone());
510          // rsaUser2 is the second receiver (cert identifief by SubjectKeyIdentifier)
511          recipients[1] = new KeyTransRecipientInfo(rsaUser2,
512                                                    CertificateIdentifier.SUBJECT_KEY_IDENTIFIER,
513                                                    (AlgorithmID)AlgorithmID.rsaEncryption.clone());
514          
515          // next recipients use key agreement (Static-Static Diffie-Hellman)
516          // the key encryption (key agreement) algorithm to use:
517          AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ssdhKeyAgreement.clone();
518          // the key wrap algorithm to use:
519          AlgorithmID keyWrapAlg = (AlgorithmID)CMSAlgorithmID.cms_HMACwithAES_wrap.clone();
520          // the length of the key encryption key to be generated:
521          int kekLength = 128;
522          // in static-static mode we may supply user keying material
523          byte[] ukm = new byte[64];
524          random.nextBytes(ukm);
525          // ssdhUser1 is originator
526          recipients[2] = new KeyAgreeRecipientInfo(ssdhUser1, 
527                                                    ssdhUser1_pk,
528                                                    KeyIdentifier.ISSUER_AND_SERIALNUMBER,
529                                                    keyEA, 
530                                                    keyWrapAlg, 
531                                                    kekLength, 
532                                                    ukm);
533          // add ssdhUser1 (originator) as recipient, too
534          ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser1, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
535          // ssdhUser2 is the recipient (cert identified by RecipientKeyIdentifier)
536          ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser2, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
537    
538          // last receiver uses a symmetric key encryption key  
539          AlgorithmID kea = (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone();
540          KEKIdentifier kekIdentifier = new KEKIdentifier(kekID);
541          recipients[3] = new KEKRecipientInfo(kekIdentifier, kea, kek);
542          
543        } catch (Exception ex) {
544          throw new CMSException("Error adding recipients: " + ex.getMessage()); 
545        }    
546        return recipients;
547      }  
548      
549      /**
550       * Parses an AuthenticatedData, decrypts the mac keys for all test recipients
551       * using the index into the recipientInfos field for identifying the recipient
552       * and verifies the content mac.
553       *
554       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
555       *
556       * @throws Exception if some error occurs during mac key decryption / mac verification
557       */ 
558      public void parseAuthenticatedDataWithRecipientInfoIndex(byte[] encodedAuthenticatedData,
559                                                               byte[] message) 
560        throws Exception {
561        
562        byte[] received_message;
563        
564        // rsaUser1
565        System.out.println("\nVerify MAC for rsaUser1:");
566        
567        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
568                                                      message,
569                                                      rsaUser1_pk,
570                                                      0);
571        System.out.print("\nContent: ");
572        System.out.println(new String(received_message));
573          
574        // rsaUser2
575        System.out.println("\nVerify MAC for rsaUser2:");
576        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
577                                                      message,
578                                                      rsaUser2_pk,
579                                                      1);
580        
581        // ssdhUser1
582        System.out.println("\nVerify MAC for ssdhUser1:");
583        
584        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
585                                                      message,
586                                                      ssdhUser1_pk,
587                                                      2);
588        System.out.print("\nContent: ");
589        System.out.println(new String(received_message));
590          
591        // ssdhUser2
592        System.out.println("\nVerify MAC for ssdhUser2:");
593        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
594                                                      message,
595                                                      ssdhUser2_pk,
596                                                      2);
597        
598        // kekUser
599        System.out.println("\nVerify MAC for kekUser:");
600        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
601                                                      message,
602                                                      kek,
603                                                      3);
604        System.out.print("\nContent: ");
605        System.out.println(new String(received_message));
606      }
607      
608      /**
609       * Parses an AuthenticatedData, decrypts the mac keys for all test recipients
610       * using their recipient identifiers for identifying the recipient
611       * and verifies the content mac.
612       *
613       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
614       *
615       * @throws Exception if some error occurs during mac key decryption / mac verification
616       */ 
617      public void parseAuthenticatedDataWithRecipientIdentifier(byte[] encodedAuthenticatedData,
618                                                                byte[] message) 
619        throws Exception {
620            
621        byte[] received_message;
622        
623        //  rsaUser1
624        System.out.println("\nVerify MAC for rsaUser1:");
625        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
626                                                      message, 
627                                                      rsaUser1_pk, 
628                                                      new IssuerAndSerialNumber(rsaUser1));
629        System.out.print("\nContent: ");
630        System.out.println(new String(received_message));
631        
632        // rsaUser2
633        System.out.println("\nVerify MAC for rsaUser2:");
634        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
635                                                      message,
636                                                      rsaUser2_pk, 
637                                                      new SubjectKeyID(rsaUser2));
638    
639        // ssdhUser1
640        System.out.println("\nVerify MAC for ssdhUser1:");
641        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
642                                                      message, 
643                                                      ssdhUser1_pk, 
644                                                      new IssuerAndSerialNumber(ssdhUser1));
645        System.out.print("\nContent: ");
646        System.out.println(new String(received_message));
647        
648        // ssdhUser2
649        System.out.println("\nVerify MAC for ssdhUser2:");
650        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
651                                                      message,
652                                                      ssdhUser2_pk, 
653                                                      new RecipientKeyIdentifier(ssdhUser2));
654        
655        // kekUser
656        System.out.println("\nVerify MAC for kekUser:");
657        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
658                                                      message,
659                                                      kek,
660                                                      new KEKIdentifier(kekID));
661        
662        System.out.print("\nDecrypted content: ");
663        System.out.println(new String(received_message));
664        
665        System.out.print("\nContent: ");
666        System.out.println(new String(received_message));
667      }
668      
669      /**
670       * Parses an AuthenticatedData, decrypts the encrypted mac keys for all test recipients
671       * using their recipient certificate (or KEK id) for identifying the recipient
672       * and verifies the content mac.
673       *
674       * @param encodedAuthenticatedData the encoded AuthenticatedData object 
675       *
676       * @throws Exception if some error occurs during mac key decryption / mac verification
677       */ 
678      public void parseAuthenticatedDataWithRecipientCertOrKEKId(byte[] encodedAuthenticatedData,
679                                                                 byte[] message) 
680        throws Exception {
681        
682        byte[] received_message;
683        // rsaUser1
684        System.out.println("\nVerify MAC for rsaUser1:");
685        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
686                                                      message, 
687                                                      rsaUser1_pk, 
688                                                      rsaUser1,
689                                                      null);
690        System.out.print("\nContent: ");
691        System.out.println(new String(received_message));
692        // rsaUser2
693        System.out.println("\nVerify MAC for rsaUser2:");
694        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
695                                                      message,
696                                                      rsaUser2_pk, 
697                                                      rsaUser2,
698                                                      null);
699        
700        
701        // ssdhUser1
702        System.out.println("\nVerify MAC for ssdhUser1:");
703        received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 
704                                                      message, 
705                                                      ssdhUser1_pk, 
706                                                      ssdhUser1,
707                                                      null);
708        System.out.print("\nContent: ");
709        System.out.println(new String(received_message));
710        // ssdhUser2
711        System.out.println("\nVerify MAC for ssdhUser2:");
712        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
713                                                      message,
714                                                      ssdhUser2_pk, 
715                                                      ssdhUser2,
716                                                      null);
717        //  kekUser
718        System.out.println("\nVerify MAC for kekUser:");
719        received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
720                                                      message,
721                                                      kek, 
722                                                      null,
723                                                      kekID);
724        System.out.print("\nDecrypted content: ");
725        System.out.println(new String(received_message));
726        
727        System.out.print("\nContent: ");
728        System.out.println(new String(received_message));
729      }
730      
731      /**
732       * Starts the test.
733       */
734      public void start() {
735         // the test message
736        String m = "This is the test message.";
737        System.out.println("Test message: \""+m+"\"");
738        System.out.println();
739        byte[] message = m.getBytes();
740    
741        try {
742          byte[] encodedAuthenticatedData;
743          
744          AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
745          int macKeyLength = 32;
746          AlgorithmID digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
747          
748          System.out.println("Stream implementation demos");
749          System.out.println("===========================");
750    
751          // implict mode; with authenticated attributes
752          System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, implicit mode]:\n");
753          encodedAuthenticatedData = createAuthenticatedDataStream(message,
754                                                                   macAlgorithm,
755                                                                   macKeyLength,
756                                                                   digestAlgorithm,
757                                                                   AuthenticatedDataOutputStream.IMPLICIT);
758          // transmit data
759          System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
760          System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
761          parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null);
762          System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
763          parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null);
764          System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
765          parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null);
766          
767          // implict mode; without authenticated attributes
768          System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n");
769          encodedAuthenticatedData = createAuthenticatedDataStream(message,
770                                                                   macAlgorithm,
771                                                                   macKeyLength,
772                                                                   null,
773                                                                   AuthenticatedDataOutputStream.IMPLICIT);
774          // transmit data
775          System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
776          System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
777          parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null);
778          System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
779          parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null);
780          System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
781          parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null);
782          
783      
784          // explict mode; with authenticated attributes
785          System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, explicit mode]:\n");
786          encodedAuthenticatedData = createAuthenticatedDataStream(message,
787                                                                   macAlgorithm,
788                                                                   macKeyLength,
789                                                                   digestAlgorithm,
790                                                                   AuthenticatedDataOutputStream.EXPLICIT);
791          // transmit data
792          System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n");
793          System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
794          parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message);
795          System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
796          parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message);
797          System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
798          parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message);
799          
800          // explict mode; without authenticated attributes
801          System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, explicit mode]:\n");
802          encodedAuthenticatedData = createAuthenticatedDataStream(message,
803                                                                   macAlgorithm,
804                                                                   macKeyLength,
805                                                                   null,
806                                                                   AuthenticatedDataOutputStream.EXPLICIT);
807          // transmit data
808          System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n");
809          System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
810          parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message);
811          System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
812          parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message);
813          System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
814          parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message);
815          
816            } catch (Exception ex) {
817              ex.printStackTrace();
818              throw new RuntimeException(ex.toString());
819            }
820      }
821      
822      /**
823       * Main method.
824       *
825       * @throws IOException
826       *            if an I/O error occurs when reading required keys
827       *            and certificates from files
828       */
829      public static void main(String argv[]) throws Exception {
830    
831            DemoUtil.initDemos();
832    
833        (new AuthenticatedDataOutputStreamDemo()).start();
834        System.out.println("Ready!");
835        System.in.read();
836      }
837    }