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/EckaEGAuthEnvelopedDataDemo.java 12    12.02.25 17:58 Dbratko $
059    // $Revision: 12 $
060    //
061    
062    
063    package demo.cms.ecc;
064    
065    import iaik.asn1.CodingException;
066    import iaik.asn1.ObjectID;
067    import iaik.asn1.structures.AlgorithmID;
068    import iaik.asn1.structures.Attribute;
069    import iaik.cms.AuthEnvelopedData;
070    import iaik.cms.AuthEnvelopedDataOutputStream;
071    import iaik.cms.AuthEnvelopedDataStream;
072    import iaik.cms.CMSAlgorithmID;
073    import iaik.cms.CMSException;
074    import iaik.cms.CertificateIdentifier;
075    import iaik.cms.CompressedData;
076    import iaik.cms.CompressedDataOutputStream;
077    import iaik.cms.CompressedDataStream;
078    import iaik.cms.ContentInfo;
079    import iaik.cms.ContentInfoOutputStream;
080    import iaik.cms.ContentInfoStream;
081    import iaik.cms.EncryptedContentInfo;
082    import iaik.cms.EncryptedContentInfoStream;
083    import iaik.cms.IssuerAndSerialNumber;
084    import iaik.cms.KeyAgreeRecipientInfo;
085    import iaik.cms.KeyIdentifier;
086    import iaik.cms.RecipientInfo;
087    import iaik.cms.RecipientKeyIdentifier;
088    import iaik.cms.SignedData;
089    import iaik.cms.SignedDataStream;
090    import iaik.cms.SignerInfo;
091    import iaik.cms.attributes.CMSContentType;
092    import iaik.cms.attributes.SigningTime;
093    import iaik.security.random.SecRandom;
094    import iaik.utils.Util;
095    import iaik.x509.X509Certificate;
096    
097    import java.io.ByteArrayInputStream;
098    import java.io.ByteArrayOutputStream;
099    import java.io.IOException;
100    import java.io.InputStream;
101    import java.security.InvalidKeyException;
102    import java.security.Key;
103    import java.security.NoSuchAlgorithmException;
104    import java.security.PrivateKey;
105    import java.security.SecureRandom;
106    import java.security.SignatureException;
107    
108    import javax.crypto.SecretKey;
109    
110    import demo.DemoUtil;
111    import demo.cms.ecc.keystore.CMSEccKeyStore;
112    
113    
114    /**
115     * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and
116     * {@link iaik.cms.SignedData}, and {@link iaik.cms.AuthEnvelopedDataStream} and
117     * {@link iaik.cms.AuthEnvelopedData} according to the BSI Technical
118     * Recommendation <a href = 
119     * "https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR03109/TR-03109-1_Anlage_CMS.pdf;jsessionid=3DD6E4FBAC90766F8E2AB3F79BB003C5.1_cid341?__blob=publicationFile&v=1" target="_blank">
120     * BSI TR-03109-1</a> for transmitting signed authenticated encrypted 
121     * data between Smart-Meter-Gateways and external market participants and the
122     * Smart Meter Gateway Administrator. 
123     * <p>
124     * This demo uses AES-GCM as specified by <a href = "http://www.ietf.org/rfc/rfc5084.txt" target="_blank">RFC 5084</a>
125     * and AES-CBC-CMAC as specified by BSI TR-03109-1 for authenticated encryption.
126     * The demo compressed the data, creates an AuthEnvelopedData object, packs it into a SignedData and 
127     * subsequently shows several ways that may be used for decrypting the content 
128     * for some particular recipient.
129     * <p>
130     * Any keys/certificates required for this demo are read from a keystore
131     * file "cmsecc.keystore" located in your current working directory. If
132     * the keystore file does not exist you can create it by running the
133     * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
134     * program. 
135     * <p>
136     * Additionally to <code>iaik_cms.jar</code> you also must have 
137     * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
138     * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
139     * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
140     * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
141     * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
142     * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
143     * in your classpath.
144     * 
145     * @see iaik.cms.AuthEnvelopedDataStream
146     * @see iaik.cms.AuthEnvelopedData
147     * @see iaik.cms.SignedDataStream
148     * @see iaik.cms.SignedData
149     * @see iaik.cms.RecipientInfo
150     * @see iaik.cms.KeyAgreeRecipientInfo
151     * 
152     */
153    public class EckaEGAuthEnvelopedDataDemo {
154      
155      // certificate of signer
156      X509Certificate[] ecdsa256bitSignerCertChain_;
157      // private key of signer
158      PrivateKey ecdsa256bitSignerPrivateKey_;
159      
160      // certificate of signer
161      X509Certificate[] ecdsa384bitSignerCertChain_;
162      // private key of signer
163      PrivateKey ecdsa384bitSignerPrivateKey_;
164      
165      //certificate of signer
166      X509Certificate[] ecdsa521bitSignerCertChain_;
167      // private key of signer
168      PrivateKey ecdsa521bitSignerPrivateKey_;
169    
170    
171      // certificate of recipient 1 (recipient 1 is signer)
172      X509Certificate ecdh256bitRecipient1Cert_;
173      // private key of recipient 1 
174      PrivateKey ecdh256bitRecipient1PrivateKey_;
175      // certificate of recipient 2
176      X509Certificate ecdh256bitRecipient2Cert_;
177      // private key of recipient 2
178      PrivateKey ecdh256bitRecipient2PrivateKey_;
179      
180      //certificate of recipient 1 (recipient 1 is signer)
181      X509Certificate ecdh384bitRecipient1Cert_;
182      // private key of recipient 1 
183      PrivateKey ecdh384bitRecipient1PrivateKey_;
184      // certificate of recipient 2
185      X509Certificate ecdh384bitRecipient2Cert_;
186      // private key of recipient 2
187      PrivateKey ecdh384bitRecipient2PrivateKey_;
188      
189      //certificate of recipient 1 (recipient 1 is signer)
190      X509Certificate ecdh521bitRecipient1Cert_;
191      // private key of recipient 1 
192      PrivateKey ecdh521bitRecipient1PrivateKey_;
193      // certificate of recipient 2
194      X509Certificate ecdh521bitRecipient2Cert_;
195      // private key of recipient 2
196      PrivateKey ecdh521bitRecipient2PrivateKey_;
197      
198      // secure random number generator
199      SecureRandom random;
200    
201      /**
202       * Setup the demo certificate chains.
203       *
204       * Keys and certificates are retrieved from the demo KeyStore ("cmsecc.keystore")
205       * file which has to be located in your current working directory and may be
206       * created by running {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore 
207       * SetupCMSEccKeyStore}.
208       *
209       * @throws IOException if an file read error occurs
210       */
211      public EckaEGAuthEnvelopedDataDemo() throws IOException {
212        
213        System.out.println();
214        System.out.println("**********************************************************************************");
215        System.out.println("*                           EckaEGAuthEnvelopedDataDemo                          *");
216        System.out.println("* (CMS AuthEnvelopedData/SignedData for  type implementation for BSI TR-03109-1) *");
217        System.out.println("**********************************************************************************");
218        System.out.println();
219        
220        ecdsa256bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN);
221        ecdsa256bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN);
222        ecdsa384bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN);
223        ecdsa384bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN);
224        ecdsa521bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN);
225        ecdsa521bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN);
226    
227        
228        ecdh256bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1)[0];
229        ecdh256bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1);
230        ecdh256bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2)[0];
231        ecdh256bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2);
232        
233        ecdh384bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1)[0];
234        ecdh384bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1);
235        ecdh384bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2)[0];
236        ecdh384bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2);
237        
238        ecdh521bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1)[0];
239        ecdh521bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1);
240        ecdh521bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2)[0];
241        ecdh521bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2);
242        random = SecRandom.getDefault();
243        
244      }
245    
246    
247      /**
248       * Creates a CMS <code>AuthEnvelopedData</code> message using class <code>AuthEnvelopedDataStream</code>.
249       *
250       * @param message the message to be authenticated-enveloped, as byte representation
251       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
252       * @param recipient1Cert the certificate of the first recipient (sender)
253       * @param recipient2Cert the certificate of the second recipient    *
254       *  
255       * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
256       * 
257       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
258       *                          be created
259       * @throws IOException if an I/O error occurs
260       */
261      public byte[] createAuthEnvelopedDataStream(byte[] message, 
262                                                  AlgorithmID contentAuthEncAlg,
263                                                  X509Certificate recipient1Cert,
264                                                  X509Certificate recipient2Cert)
265        throws CMSException, IOException {
266        
267        System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
268    
269        // we are testing the stream interface
270        ByteArrayInputStream is = new ByteArrayInputStream(message);
271        // create a new AuthEnvelopedData object 
272        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(ObjectID.cms_compressedData, is, contentAuthEncAlg);
273        
274        //  create some authenticated attributes
275        try {
276          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
277          authEnvelopedData.setAuthenticatedAttributes(attributes);
278        } catch (Exception ex) {
279          throw new CMSException("Error creating attribute: " + ex.toString());   
280        }
281    
282        // create the recipient infos
283        RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
284        // specify the recipients of the encrypted message
285        authEnvelopedData.setRecipientInfos(recipients);
286    
287        // wrap into ContentInfo
288        ContentInfoStream contentInfo = new ContentInfoStream(authEnvelopedData);
289        ByteArrayOutputStream os = new ByteArrayOutputStream();
290        contentInfo.writeTo(os);
291        return os.toByteArray();
292      }
293      
294      /**
295       * Creates a CMS <code>AuthEnvelopedData</code> message using class
296       * <code>AuthEnvelopedDataOutputStream</code>. The content data is 
297       * compressed inside this method.
298       *
299       * @param message the message to be authenticated-enveloped, as byte representation
300       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
301       * @param recipient1Cert the certificate of the first recipient (sender)
302       * @param recipient2Cert the certificate of the second recipient  
303       * 
304       * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
305       * 
306       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
307       *                          be created
308       * @throws IOException if an I/O error occurs
309       */
310      public byte[] createAuthEnvelopedDataOutputStream(byte[] message, 
311                                                        AlgorithmID contentAuthEncAlg,
312                                                        X509Certificate recipient1Cert,
313                                                        X509Certificate recipient2Cert)
314        throws CMSException, IOException {
315        
316        System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
317    
318        // we are testing the stream interface
319        ByteArrayInputStream is = new ByteArrayInputStream(message);
320        // the stream to which to write the AuthEnvelopedData
321        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
322        AuthEnvelopedDataOutputStream authEnvelopedData;
323    
324        //  wrap AuthEnvelopedData into a ContentInfo 
325        ContentInfoOutputStream contentInfoStream = 
326          new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
327       
328        // create a new AuthEnvelopedData object 
329        authEnvelopedData = new AuthEnvelopedDataOutputStream(contentInfoStream, 
330                                                              contentAuthEncAlg);
331        
332            // create the recipient infos
333        RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
334        // specify the recipients of the encrypted message
335        authEnvelopedData.setRecipientInfos(recipients);
336        
337        //  create some authenticated attributes
338        try {
339          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
340          authEnvelopedData.setAuthenticatedAttributes(attributes);
341        } catch (Exception ex) {
342          throw new CMSException("Error creating attribute: " + ex.toString());   
343        }
344    
345        // compress message
346        CompressedDataOutputStream compressedData = 
347            new CompressedDataOutputStream(authEnvelopedData,
348                                           (AlgorithmID)CMSAlgorithmID.zlib_compress.clone());
349    
350        int blockSize = 16; // in real world we would use a block size like 2048
351        //  write in the data to be encrypted
352        byte[] buffer = new byte[blockSize];
353        int bytesRead;
354        while ((bytesRead = is.read(buffer)) != -1) {
355          compressedData.write(buffer, 0, bytesRead);
356        }
357        
358        // closing the stream finishes encryption and closes the underlying stream
359        compressedData.close();
360       // authEnvelopedData.close();
361        return resultStream.toByteArray();
362      }
363      
364    
365      /**
366       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
367       * the recipient identified by its index into the recipientInfos field and verifies
368       * the message authentication code.
369       * <p>
370       * This way of decrypting the content may be used for any type of RecipientInfo
371       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
372       * know at what index of the recipientInfo field the RecipientInfo for the 
373       * particular recipient in mind can be found. If the recipient in mind uses
374       * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
375       * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
376       * keys for more than only one recipient; since the recipientInfoIndex only
377       * specifies the RecipientInfo but not the encrypted content encryption key 
378       * -- if there are more than only one -- repeated decryption runs may be
379       * required as long as the decryption process completes successfully.
380       *
381       * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
382       * @param key the key to decrypt the message
383       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
384       *                        to which the specified key belongs
385       *
386       * @return the recovered message, as byte array
387       * @throws CMSException if the message cannot be recovered or MAC verification fails
388       * @throws IOException if a stream read/write error occurs
389       */
390      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
391        throws CMSException, IOException {
392    
393        // create the AuthEnvelopedData object from a BER encoded byte array
394        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
395        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
396    
397        System.out.println("Information about the authenticated encrypted data:");
398        EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
399        System.out.println("Content type: "+eci.getContentType().getName());
400        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
401    
402        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
403        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
404        
405        // for demonstration purposes we only look one time for all recipients included:
406        if (recipientInfoIndex == 0) {
407          int k = 0;
408          for (int i=0; i<recipients.length; i++) {
409            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
410            for (int j = 0; j < recipientIDs.length; j++) {
411              System.out.println("Recipient "+(++k)+":");
412              System.out.println(recipientIDs[j]);
413            }   
414          }
415        }
416        // decrypt the message for the first recipient and verify mac
417        try {
418          authEnvelopedData.setupCipher(key, recipientInfoIndex);
419          InputStream decrypted = authEnvelopedData.getInputStream();
420          ByteArrayOutputStream os = new ByteArrayOutputStream();
421          Util.copyStream(decrypted, os, null);
422          byte[] content = os.toByteArray();
423    
424          // get authenticated attributes
425          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
426          if (contentTypeAttribute != null) {
427            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
428            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
429          }
430          
431          return content;
432        } catch (InvalidKeyException ex) {
433          throw new CMSException("Private key error: "+ex.toString());
434        } catch (NoSuchAlgorithmException ex) {
435          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
436        } catch (CodingException ex) {
437          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
438        }
439      }
440      
441      /**
442       * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
443       * the recipient identified by recipient identifier and verifies the message 
444       * authentication code.
445       * <p>
446       * This way of decrypting the content may be used for any type of RecipientInfo
447       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
448       * recipient in mind is identified by its recipient identifier.
449       *
450       * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
451       * @param key the key to decrypt the message
452       * @param recipientID the recipient identifier uniquely identifying the key of the
453       *        recipient
454       *
455       * @return the recovered message, as byte array
456       * @throws CMSException if the message cannot be recovered
457       * @throws IOException if a stream read/write error occurs
458       */
459      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
460        throws CMSException, IOException {
461    
462        // create the AuthEnvelopedData object from a DER encoded byte array
463        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
464        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
465    
466        System.out.println("Information about the encrypted data:");
467        EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
468        System.out.println("Content type: "+eci.getContentType().getName());
469        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
470        
471        // get the right RecipientInfo
472        System.out.println("\nSearch for RecipientInfo:");
473        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
474        if (recipient != null) {
475          System.out.println("RecipientInfo: " + recipient);   
476        } else {
477          throw new CMSException("No recipient with ID: " + recipientID);
478        }    
479        // decrypt the content encryption key and the content; verify mac
480        try {
481          System.out.println("Decrypt encrypted content encryption key...");
482          SecretKey cek = recipient.decryptKey(key, recipientID);
483          System.out.println("Decrypt content with decrypted content encryption key...");
484          authEnvelopedData.setupCipher(cek);
485          InputStream decrypted = authEnvelopedData.getInputStream();
486          ByteArrayOutputStream os = new ByteArrayOutputStream();
487          Util.copyStream(decrypted, os, null);
488          byte[] content = os.toByteArray();
489    
490          // get authenticated attributes
491          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
492          if (contentTypeAttribute != null) {
493            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
494            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
495          }
496          
497          return content;
498        } catch (InvalidKeyException ex) {
499          throw new CMSException("Private key error: "+ex.toString());
500        } catch (NoSuchAlgorithmException ex) {
501          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
502        } catch (CodingException ex) {
503          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
504        }
505      }
506      
507      /**
508       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
509       * the recipient identified by its recipient certificate and verifies the message 
510       * authentication code.
511       * <p>
512       *
513       * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
514       * @param key the key to decrypt the message
515       * @param recipientCert the certificate of the recipient having a RecipientInfo of
516       *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
517       *
518       * @return the recovered message, as byte array
519       * @throws CMSException if the message cannot be recovered
520       * @throws IOException if a stream read/write error occurs
521       */
522      public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert)
523        throws CMSException, IOException {
524    
525        // create the AuthEnvelopedData object from a BER encoded byte array
526        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
527        AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
528    
529        System.out.println("Information about the encrypted data:");
530        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
531        System.out.println("Content type: "+eci.getContentType().getName());
532        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
533        
534        // decrypt the content encryption key and the content; verify mac
535        try {
536          System.out.println("Decrypt the content...");
537          authEnvelopedData.setupCipher(key, recipientCert);
538          InputStream decrypted = authEnvelopedData.getInputStream();
539          ByteArrayOutputStream os = new ByteArrayOutputStream();
540          Util.copyStream(decrypted, os, null);
541          byte[] content = os.toByteArray();
542    
543          // get authenticated attributes
544          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
545          if (contentTypeAttribute != null) {
546            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
547            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
548          }
549          
550          return content;
551        } catch (InvalidKeyException ex) {
552          throw new CMSException("Private key error: "+ex.toString());
553        } catch (NoSuchAlgorithmException ex) {
554          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
555        } catch (CodingException ex) {
556          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
557        }
558      }
559    
560    
561      // non stream
562    
563      /**
564       * Creates a CMS <code>AuthEnvelopedData</code> message.
565       * 
566       * @param message the message to be enveloped, as byte representation
567       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
568       * @param recipient1Cert the certificate of the first recipient (sender)
569       * @param recipient2Cert the certificate of the second recipient  
570       * 
571       * @return the encoded <code>AuthEnvelopedData</code>, as byte array
572       * 
573       * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
574       *                          be created
575       */
576      public byte[] createAuthEnvelopedData(
577          byte[] message, 
578          AlgorithmID contentAuthEncAlg,
579          X509Certificate recipient1Cert,
580          X509Certificate recipient2Cert)
581        throws CMSException {
582        
583        System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
584        
585        AuthEnvelopedData authEnvelopedData;
586    
587        // create a new AuthEnvelopedData object
588        authEnvelopedData = new AuthEnvelopedData(ObjectID.cms_compressedData, message, contentAuthEncAlg);
589        
590        //  create some authenticated attributes
591        try {
592          Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
593          authEnvelopedData.setAuthenticatedAttributes(attributes);
594        } catch (Exception ex) {
595          throw new CMSException("Error creating attribute: " + ex.toString());   
596        }
597        
598        // set the RecipientInfos
599        RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
600        authEnvelopedData.setRecipientInfos(recipients);
601        authEnvelopedData.getEncryptedContentInfo().setBlockSize(2048);
602    
603        // wrap into ContentInfo
604        ContentInfo contentInfo = new ContentInfo(authEnvelopedData);
605        // return encoded EnvelopedData
606        return contentInfo.getEncoded();
607      }
608    
609    
610      /**
611       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
612       * the recipient identified by its index into the recipientInfos field and verifies
613       * the message authentication code.
614       * <p>
615       * This way of decrypting the content may be used for any type of RecipientInfo
616       * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
617       * know at what index of the recipientInfo field the RecipientInfo for the 
618       * particular recipient in mind can be found. If the recipient in mind uses
619       * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
620       * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
621       * keys for more than only one recipient; since the recipientInfoIndex only
622       * specifies the RecipientInfo but not the encrypted content encryption key 
623       * -- if there are more than only one -- repeated decryption runs may be
624       * required as long as the decryption process completes successfully.
625       *
626       * @param enc the encoded <code>AuthEnvelopedData</code>
627       * 
628       * @param key the key to decrypt the message
629       * 
630       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
631       *                    to which the specified key belongs
632       *
633       * @return the recovered message, as byte array
634       * 
635       * @throws CMSException if the message cannot be recovered
636       * @throws IOException if an I/O error occurs
637       */
638      public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
639        throws CMSException, IOException {
640        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
641        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
642    
643        System.out.println("Information about the encrypted data:");
644        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
645        System.out.println("Content type: "+eci.getContentType().getName());
646        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
647    
648        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
649        RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
650        
651        // for demonstration purposes we only look one time for all recipients included:
652        if (recipientInfoIndex == 0) {
653          int k = 0;
654          for (int i=0; i<recipients.length; i++) {
655            KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
656            for (int j = 0; j < recipientIDs.length; j++) {
657              System.out.println("Recipient "+(++k)+":");
658              System.out.println(recipientIDs[j]);
659            }   
660          }
661        }
662        
663        // decrypt the message and verify the mac
664        try {
665          authEnvelopedData.setupCipher(key, recipientInfoIndex);
666          byte[] content = authEnvelopedData.getContent();
667    
668          // get authenticated attributes
669          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
670          if (contentTypeAttribute != null) {
671            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
672            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
673          }
674          
675          return content;
676        } catch (InvalidKeyException ex) {
677          throw new CMSException("Private key error: "+ex.toString());
678        } catch (NoSuchAlgorithmException ex) {
679          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
680        } catch (CodingException ex) {
681          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
682        }
683      }
684      
685      /**
686       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</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 enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
694       * @param key the key to decrypt the message
695       * @param recipientID the recipient identifier uniquely identifying the key of the
696       *        recipient
697       *
698       * @return the recovered message, as byte array
699       * @throws CMSException if the message cannot be recovered
700       * @throws IOException if an I/O error occurs
701       */
702      public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
703        throws CMSException, IOException {
704        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
705        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
706    
707        System.out.println("Information about the encrypted data:");
708        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
709        System.out.println("Content type: "+eci.getContentType().getName());
710        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
711    
712        // get the right RecipientInfo
713        System.out.println("\nSearch for RecipientInfo:");
714        RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
715        if (recipient != null) {
716          System.out.println("RecipientInfo: " + recipient);   
717        } else {
718          throw new CMSException("No recipient with ID: " + recipientID);
719        }    
720        // decrypt the content encryption key and the content
721        try {
722          byte[] content = null;
723          System.out.println("Decrypt encrypted content encryption key...");
724          SecretKey cek = recipient.decryptKey(key, recipientID);
725         // decrypt content and verify mac
726          System.out.println("Decrypt content with decrypted content encryption key...");
727          authEnvelopedData.setupCipher(cek);
728          content = authEnvelopedData.getContent();
729          // get authenticated attributes
730          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
731          if (contentTypeAttribute != null) {
732            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
733            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
734          }
735          
736          return content;
737        } catch (InvalidKeyException ex) {
738          throw new CMSException("Private key error: "+ex.toString());
739        } catch (NoSuchAlgorithmException ex) {
740          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
741        } catch (CodingException ex) {
742          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
743        }
744      }
745      
746      /**
747       * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
748       * the recipient identified by its recipient certificate.
749       *
750       * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
751       * @param key the key to decrypt the message
752       * @param recipientCert the certificate of the recipient having a RecipientInfo of
753       *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
754       *
755       * @return the recovered message, as byte array
756       * @throws CMSException if the message cannot be recovered
757       */
758      public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 
759        throws CMSException, IOException {
760        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
761        AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
762    
763        System.out.println("Information about the encrypted data:");
764        EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
765        System.out.println("Content type: "+eci.getContentType().getName());
766        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
767    
768        // decrypt the content encryption key and the content
769        try {
770          System.out.println("Decrypt the content and verify mac...");
771          // decrypt content and verify mac
772          authEnvelopedData.setupCipher(key, recipientCert);
773          
774          byte[] content = authEnvelopedData.getContent();
775    
776          // get authenticated attributes
777          Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
778          if (contentTypeAttribute != null) {
779            CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
780            System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
781          }
782          
783          return content;
784        } catch (InvalidKeyException ex) {
785          throw new CMSException("Private key error: "+ex.toString());
786        } catch (NoSuchAlgorithmException ex) {
787          throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
788        } catch (CodingException ex) {
789          throw new CMSException("Error reading authenticated attributes: "+ex.toString());
790        }
791    
792      }
793      
794      /**
795       * Creates the RecipientInfos.
796       * 
797       * @param contentAuthEncAlg the content encryption algorithm
798       * @param recipient1Cert the certificate of the first recipient (sender)
799       * @param recipient2Cert the certificate of the second recipient 
800       *
801       * @return the RecipientInfos created, one
802       *         KeyAgreeRecipientInfo (for two recipients with same domain
803       *         parameters)
804       *
805       * @throws CMSException if an error occurs when creating the recipient infos
806       */
807      public RecipientInfo[] createRecipients(
808          AlgorithmID contentAuthEncAlg,
809          X509Certificate recipient1Cert,
810          X509Certificate recipient2Cert) throws CMSException {
811        
812        RecipientInfo[] recipients = new RecipientInfo[1];
813        try {
814       
815          // next recipients use key agreement
816          // the key encryption (key agreement) algorithm to use:
817          AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA256.clone();
818          // the key wrap algorithm to use:
819          AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
820          // the length of the key encryption key to be generated:
821          int kekLength = 128;
822          if ((contentAuthEncAlg.equals(AlgorithmID.aes192_GCM) ||
823              (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_192)))) {
824            keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA384.clone();
825            keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
826            kekLength = 192;
827          } else if ((contentAuthEncAlg.equals(AlgorithmID.aes256_GCM) ||
828              (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_256)))) {
829            keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA512.clone();
830            keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
831            kekLength = 256;
832          }
833          recipients[0] = new KeyAgreeRecipientInfo(keyEA, keyWrapAlg, kekLength);
834          // ecdhUser1 is the third receiver  (cert identified by IssuerAndSerialNumber)
835          ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient1Cert, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
836          // ecdhUser2 is the fourth receiver (cert identified by RecipientKeyIdentifier)
837          ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient2Cert, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
838          
839        } catch (Exception ex) {
840          throw new CMSException("Error adding recipients: " + ex.getMessage()); 
841        }    
842        return recipients;
843      }  
844      
845      /**
846       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
847       * using the index into the recipientInfos field for identifying the recipient.
848       *
849       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
850       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
851       * @param recipient1PrivateKey the private key of the first recipient (sender)
852       * @param recipient2PrivateKey the private key of the second recipient
853       *
854       * @throws Exception if some error occurs during decoding/decryption
855       */ 
856      public void parseAuthEnvelopedDataWithRecipientInfoIndex(
857          boolean stream, 
858          byte[] encodedAuthEnvelopedData,
859          PrivateKey recipient1PrivateKey,
860          PrivateKey recipient2PrivateKey) throws Exception {
861        byte[] receivedMessage;
862        if (stream) {
863          // ecdhUser1
864          System.out.println("\nDecrypt for ecdhUser1:");
865          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, 0);
866          // decompress
867          receivedMessage = getCompressedDataStream(receivedMessage);
868          System.out.print("\nDecrypted content: ");
869          System.out.println(new String(receivedMessage));
870          // ecdhUser2
871          System.out.println("\nDecrypt for ecdhUser2:");
872          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, 0);
873          // decompress
874          receivedMessage = getCompressedDataStream(receivedMessage);
875          System.out.print("\nDecrypted content: ");
876          System.out.println(new String(receivedMessage));
877    
878        } else {
879          // ecdhUser1
880          System.out.println("\nDecrypt for ecdhUser1:");
881          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, 0);
882          // decompress
883          receivedMessage = getCompressedData(receivedMessage);
884          System.out.print("\nDecrypted content: ");
885          System.out.println(new String(receivedMessage));
886          // ecdhUser2
887          System.out.println("\nDecrypt for ecdhUser2:");
888          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, 0);
889          // decompress
890          receivedMessage = getCompressedData(receivedMessage);
891          System.out.print("\nDecrypted content: ");
892          System.out.println(new String(receivedMessage));
893    
894        }    
895      }
896      
897      /**
898       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
899       * using their recipient identifiers for identifying the recipient.
900       *
901       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
902       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
903       * @param recipient1PrivateKey the private key of the first recipient (sender)
904       * @param recipient1Cert the certificate of the first recipient (sender)
905       * @param recipient2PrivateKey the private key of the second recipient
906       * @param recipient2Cert the certificate of the second recipient 
907       *
908       * @throws Exception if some error occurs during decoding/decryption
909       */ 
910      public void parseAuthEnvelopedDataWithRecipientIdentifier(
911          boolean stream, 
912          byte[] encodedAuthEnvelopedData,
913          PrivateKey recipient1PrivateKey,
914          X509Certificate recipient1Cert,
915          PrivateKey recipient2PrivateKey,
916          X509Certificate recipient2Cert) throws Exception {
917        byte[] receivedMessage;
918        if (stream) {
919          // ecdhUser1
920          System.out.println("\nDecrypt for ecdhUser1:");
921          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert));
922          // decompress
923          receivedMessage = getCompressedDataStream(receivedMessage);
924          System.out.print("\nDecrypted content: ");
925          System.out.println(new String(receivedMessage));
926          // ecdhUser2
927          System.out.println("\nDecrypt for ecdhUser2:");
928          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert));
929          // decompress
930          receivedMessage = getCompressedDataStream(receivedMessage);      
931          System.out.print("\nDecrypted content: ");
932          System.out.println(new String(receivedMessage));
933        } else {
934          // ecdhUser1
935          System.out.println("\nDecrypt for ecdhUser1:");
936          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert));
937          // decompress
938          receivedMessage = getCompressedData(receivedMessage);      
939          System.out.print("\nDecrypted content: ");
940          System.out.println(new String(receivedMessage));
941          // ecdhUser2
942          System.out.println("\nDecrypt for ecdhUser2:");
943          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert));
944          // decompress
945          receivedMessage = getCompressedData(receivedMessage);
946          System.out.print("\nDecrypted content: ");
947          System.out.println(new String(receivedMessage));
948        }    
949      }
950      
951      /**
952       * Parses an AuthEnvelopedData and decrypts the content for all test recipients
953       * using their recipient certificate for identifying the recipient.
954       *
955       * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
956       * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
957       * @param recipient1PrivateKey the private key of the first recipient (sender)
958       * @param recipient1Cert the certificate of the first recipient (sender)
959       * @param recipient2PrivateKey the private key of the second recipient
960       * @param recipient2Cert the certificate of the second recipient
961       *
962       * @throws Exception if some error occurs during decoding/decryption
963       */ 
964      public void parseAuthEnvelopedDataWithRecipientCert(
965          boolean stream, 
966          byte[] encodedAuthEnvelopedData,
967          PrivateKey recipient1PrivateKey,
968          X509Certificate recipient1Cert,
969          PrivateKey recipient2PrivateKey,
970          X509Certificate recipient2Cert) throws Exception {
971        byte[] receivedMessage;
972        if (stream) {
973          // ecdhUser1
974          System.out.println("\nDecrypt for ecdhUser1:");
975          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert);
976          // decompress
977          receivedMessage = getCompressedDataStream(receivedMessage);
978          System.out.print("\nDecrypted content: ");
979          System.out.println(new String(receivedMessage));
980          // ecdhUser2
981          System.out.println("\nDecrypt for ecdhUser2:");
982          receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert);
983          // decompress
984          receivedMessage = getCompressedDataStream(receivedMessage);      
985          System.out.print("\nDecrypted content: ");
986          System.out.println(new String(receivedMessage));
987        } else {
988          // ecdhUser1
989          System.out.println("\nDecrypt for ecdhUser1:");
990          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert);
991          // decompress
992          receivedMessage = getCompressedData(receivedMessage);      
993          System.out.print("\nDecrypted content: ");
994          System.out.println(new String(receivedMessage));
995          // ecdhUser2
996          System.out.println("\nDecrypt for ecdhUser2:");
997          receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert);
998          // decompress
999          receivedMessage = getCompressedData(receivedMessage);
1000          System.out.print("\nDecrypted content: ");
1001          System.out.println(new String(receivedMessage));
1002        }    
1003      }
1004      
1005      /**
1006       * Creates an ECDSA signed CMS <code>SignedDataStream</code> object and wraps it by a
1007       * CMS <code>ContentInfoStream</code>.
1008       *
1009       * @param message the message to be signed, as byte representation
1010       * @param mode the transmission mode, either IMPLICIT or EXPLICIT
1011       * @param hashAlgorithm the hash algorithm to be used
1012       * @param signatureAlgorithm the signature algorithm to be used
1013       * @param signerKey the private key of the signer
1014       * @param certificates the certificate chain of the signer
1015       * 
1016       * @return the DER encoding of the <code>ContentInfo</code> object just created
1017       * 
1018       * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code>
1019       *            object cannot be created
1020       * @throws IOException if an I/O related error occurs
1021       */
1022      public byte[] createSignedDataStream(byte[] message, 
1023                                           int mode,
1024                                           AlgorithmID hashAlgorithm,
1025                                           AlgorithmID signatureAlgorithm,
1026                                           PrivateKey signerKey,
1027                                           X509Certificate[] certificates) 
1028        throws CMSException, IOException  {
1029        
1030        System.out.print("Create a new message signed with " + signatureAlgorithm.getName());
1031       
1032        // we are testing the stream interface
1033        ByteArrayInputStream is = new ByteArrayInputStream(message);
1034        // create a new SignedData object which includes the data
1035        SignedDataStream signed_data = new SignedDataStream(is, ObjectID.cms_authEnvelopedData, mode);
1036        
1037        // SignedData shall include the certificate chain for verifying
1038        signed_data.setCertificates(certificates);
1039    
1040        // cert at index 0 is the user certificate
1041        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]);
1042    
1043        // create a new SignerInfo
1044        AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone();
1045        // CMS-ECDSA requires to encode the parameter field as NULL (see RFC 3278)
1046        ecdsaSig.encodeAbsentParametersAsNull(true);
1047        SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey);
1048        
1049        try {
1050          // create some signed attributes
1051          // the message digest attribute is automatically added
1052          Attribute[] attributes = new Attribute[2];
1053          // content type is data
1054          CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData);
1055          attributes[0] = new Attribute(contentType);
1056          // signing time is now
1057          SigningTime signingTime = new SigningTime();
1058          attributes[1] = new Attribute(signingTime);
1059      
1060          // set the attributes
1061          signer_info.setSignedAttributes(attributes);
1062        } catch (Exception ex) {
1063          throw new CMSException("Error adding attributes: " + ex.toString());
1064        }
1065        
1066        // finish the creation of SignerInfo by calling method addSigner
1067        try {
1068          signed_data.addSignerInfo(signer_info);
1069        } catch (NoSuchAlgorithmException ex) {
1070          throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
1071        }
1072    
1073        // write the data through SignedData to any out-of-band place
1074        if (mode == SignedDataStream.EXPLICIT) {
1075          InputStream data_is = signed_data.getInputStream();
1076          byte[] buf = new byte[1024];
1077          int r;
1078          while ((r = data_is.read(buf)) > 0) {
1079            ;   // skip data
1080          }  
1081        }
1082    
1083        signed_data.setBlockSize(2048);
1084         // create the ContentInfo
1085        ContentInfoStream cis = new ContentInfoStream(signed_data);
1086        // return the SignedData as DER encoded byte array with block size 2048
1087        ByteArrayOutputStream os = new ByteArrayOutputStream();
1088        cis.writeTo(os);
1089        return os.toByteArray();
1090      }
1091      
1092      
1093      /**
1094       * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code> 
1095       * object and verifies the signature.
1096       *
1097       * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 
1098       *                   object as BER encoded byte array
1099       * @param message the the message which was transmitted out-of-band (explicit signed)
1100       * @param certificates the certificate of the signer (used for alternative signature verification)
1101       * 
1102       * @return the inherent message as byte array
1103       * 
1104       * @throws CMSException if any signature does not verify
1105       * @throws IOException if an I/O related error occurs
1106       */
1107      public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates) 
1108        throws CMSException, IOException {
1109    
1110        // we are testing the stream interface
1111        ByteArrayInputStream is = new ByteArrayInputStream(signedData);
1112    
1113        SignedDataStream signed_data = new SignedDataStream(is);
1114    
1115        if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
1116          // in explicit mode explicitly supply the content for hash computation
1117          signed_data.setInputStream(new ByteArrayInputStream(message));
1118        }
1119    
1120        // get an InputStream for reading the signed content and update hash computation
1121        InputStream data = signed_data.getInputStream();
1122        ByteArrayOutputStream os = new ByteArrayOutputStream();
1123        Util.copyStream(data, os, null);
1124    
1125        System.out.println("SignedData contains the following signer information:");
1126        SignerInfo[] signer_infos = signed_data.getSignerInfos();
1127        
1128        int numberOfSignerInfos = signer_infos.length;
1129        if (numberOfSignerInfos == 0) {
1130          String warning = "Warning: Unsigned message (no SignerInfo included)!";  
1131          System.err.println(warning);
1132          throw new CMSException(warning);
1133        } else {
1134          for (int i = 0; i < numberOfSignerInfos; i++) {
1135            try {
1136              // verify the signed data using the SignerInfo at index i
1137              X509Certificate signer_cert = signed_data.verify(i);
1138              // if the signature is OK the certificate of the signer is returned
1139              System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
1140              // check for some included attributes
1141              SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
1142              if (signingTime != null) {
1143                System.out.println("This message has been signed at " + signingTime.get());
1144              } 
1145              CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
1146              if (contentType != null) {
1147                System.out.println("The content has CMS content type " + contentType.get().getName());
1148              }  
1149            } catch (SignatureException ex) {
1150              // if the signature is not OK a SignatureException is thrown
1151              System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
1152              throw new CMSException(ex.toString());
1153            }  
1154          }  
1155          // now check alternative signature verification
1156          System.out.println("Now check the signature assuming that no certs have been included:");
1157          try {
1158            SignerInfo signer_info = signed_data.verify(certificates[0]);
1159            // if the signature is OK the certificate of the signer is returned
1160            System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1161          } catch (SignatureException ex) {
1162            // if the signature is not OK a SignatureException is thrown
1163            System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN());
1164            throw new CMSException(ex.toString());
1165          }
1166          // in practice we also would validate the signer certificate(s)  
1167        }      
1168        return os.toByteArray();
1169      }
1170      
1171      
1172      /**
1173       * Creates an ECDSA signed CMS <code>SignedData</code> object and wraps it by a CMS
1174       * <code>ContentInfo</code> object.
1175       * <p>
1176       *
1177       * @param message the message to be signed, as byte representation
1178       * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
1179       * @param hashAlgorithm the hash algorithm to be used
1180       * @param signatureAlgorithm the signature algorithm to be used
1181       * @param signerKey the private key of the signer
1182       * @param certificates the certificate chain of the signer
1183       * 
1184       * @return the DER encoded <code>SignedData</code>-<code>ContentInfo</code> object
1185       *  
1186       * @throws CMSException if the <code>SignedData</code>-<code>ContentInfo</code> object cannot
1187       *                          be created
1188       * @throws IOException if an I/O related error occurs
1189       */
1190      public byte[] createSignedData(byte[] message, 
1191                                     int mode,
1192                                     AlgorithmID hashAlgorithm,
1193                                     AlgorithmID signatureAlgorithm,
1194                                     PrivateKey signerKey,
1195                                     X509Certificate[] certificates) 
1196        throws CMSException, IOException  {
1197        
1198        System.out.println("Create a new message signed with " + signatureAlgorithm.getName());
1199      
1200        // create a new SignedData object which includes the data
1201        SignedData signed_data = new SignedData(message, ObjectID.cms_authEnvelopedData, mode);
1202        
1203        // SignedData shall include the certificate chain for verifying
1204        signed_data.setCertificates(certificates);
1205      
1206        // cert at index 0 is the user certificate
1207        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]);
1208    
1209        // create a new SignerInfo
1210        AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone();
1211        // CMS-ECC requires that the parameters field is encoded as ASN.1 NULL object (see RFC 3278)
1212        ecdsaSig.encodeAbsentParametersAsNull(true);
1213        SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey);
1214        
1215        try {
1216          // create some signed attributes
1217          // the message digest attribute is automatically added
1218          Attribute[] attributes = new Attribute[2];
1219          // content type is data
1220          CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData);
1221          attributes[0] = new Attribute(contentType);
1222          // signing time is now
1223          SigningTime signingTime = new SigningTime();
1224          attributes[1] = new Attribute(signingTime);
1225      
1226          // set the attributes
1227          signer_info.setSignedAttributes(attributes);
1228        } catch (Exception ex) {
1229          throw new CMSException("Error adding attributes: " + ex.toString());
1230        }
1231        
1232        // finish the creation of SignerInfo by calling method addSigner
1233        try {
1234          signed_data.addSignerInfo(signer_info);
1235        } catch (NoSuchAlgorithmException ex) {
1236          throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
1237        }
1238    
1239        ContentInfo ci = new ContentInfo(signed_data); 
1240        return ci.getEncoded();
1241      }
1242      
1243      
1244      /**
1245       * Parses a CMS <code>ContentInfo</code> holding a <code>SignedData</code> 
1246       * object and verifies the signature.
1247       *
1248       * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 
1249       *                   object as DER encoded byte array
1250       * @param message the message which was transmitted out-of-band (explicit signed)
1251       * @param certificates the certificate of the signer (used for alternative signature verification) 
1252       * 
1253       * @return the inherent message as byte array
1254       * 
1255       * @throws CMSException if any signature does not verify
1256       * @throws IOException if an I/O related error occurs
1257       */
1258      public byte[] getSignedData(byte[] signedData, byte[] message, X509Certificate[] certificates) 
1259        throws CMSException, IOException {
1260        
1261        ByteArrayInputStream is = new ByteArrayInputStream(signedData);
1262        // create the SignedData object
1263        SignedData signed_data = new SignedData(is);
1264        
1265        if (signed_data.getMode() == SignedData.EXPLICIT) {
1266          // in explcit mode explictly supply the content data to do the hash calculation
1267          signed_data.setContent(message);
1268        }
1269        
1270        System.out.println("SignedData contains the following signer information:");
1271        SignerInfo[] signer_infos = signed_data.getSignerInfos();
1272        
1273        int numberOfSignerInfos = signer_infos.length;
1274        if (numberOfSignerInfos == 0) {
1275          String warning = "Warning: Unsigned message (no SignerInfo included)!";  
1276          System.err.println(warning);
1277          throw new CMSException(warning);
1278        } else {
1279          for (int i = 0; i < numberOfSignerInfos; i++) {
1280            try {
1281              // verify the signed data using the SignerInfo at index i
1282              X509Certificate signer_cert = signed_data.verify(i);
1283              // if the signature is OK the certificate of the signer is returned
1284              System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
1285              // check some attributes
1286              SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
1287              if (signingTime != null) {
1288                System.out.println("This message has been signed at " + signingTime.get());
1289              } 
1290              CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
1291              if (contentType != null) {
1292                System.out.println("The content has CMS content type " + contentType.get().getName());
1293              }
1294            } catch (SignatureException ex) {
1295              // if the signature is not OK a SignatureException is thrown
1296              System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
1297              throw new CMSException(ex.toString());
1298            } 
1299          }      
1300        
1301          // now check alternative signature verification
1302          System.out.println("Now check the signature assuming that no certs have been included:");
1303          try {
1304            SignerInfo signer_info = signed_data.verify(certificates[0]);
1305            // if the signature is OK the certificate of the signer is returned
1306            System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1307          } catch (SignatureException ex) {
1308            // if the signature is not OK a SignatureException is thrown
1309            System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN());
1310            throw new CMSException(ex.toString());
1311          }
1312          // in practice we also would validate the signer certificate(s)  
1313        }      
1314        return signed_data.getContent();
1315      }
1316      
1317      /**
1318       * Creates a CMS <code>CompressedData</code> object.
1319       * <p>
1320       * @param message the message to be compressed, as byte representation
1321       *
1322       * @return the BER encoding of the <code>CompressedData</code> object just created
1323       *
1324       * @throws CMSException if the <code>CompressedData</code> object cannot
1325       *                          be created
1326       * @throws IOException if an I/O error occurs
1327       * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1328       */
1329      public byte[] createCompressedDataStream(byte[] message) 
1330        throws CMSException, IOException, NoSuchAlgorithmException {
1331    
1332        System.out.println("Create a new CompressedData message");
1333    
1334        // we are testing the stream interface
1335        ByteArrayInputStream is = new ByteArrayInputStream(message);
1336    
1337        // create a new CompressedData object 
1338        CompressedDataStream compressedData = new CompressedDataStream(is, 
1339                                                                       (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(),
1340                                                                       CompressedDataStream.IMPLICIT);
1341    
1342        // for testing return the CompressedData as BER encoded byte array with block size of 4
1343        ByteArrayOutputStream os = new ByteArrayOutputStream();
1344        compressedData.setBlockSize(4);
1345        ContentInfoStream cis = new ContentInfoStream(compressedData);
1346        cis.writeTo(os);
1347        return os.toByteArray();
1348      }
1349    
1350      /**
1351       * Parses a CMS <code>CompressedData</code> object.
1352       *
1353       * @param encoding  the <code>CompressedData</code> object as BER encoded byte array
1354       *
1355       * @return the decompressed message as byte array
1356       *
1357       * @throws CMSException if the CompressedData cannot be parsed
1358       * @throws IOException if an I/O error occurs
1359       * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1360       */
1361      public byte[] getCompressedDataStream(byte[] encoding) 
1362        throws CMSException, IOException, NoSuchAlgorithmException {
1363    
1364        System.out.println("Parse CompressedData message.");
1365        // we are testing the stream interface
1366        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
1367        // create the CompressedData object
1368        CompressedDataStream compressedData = new CompressedDataStream(is);
1369        // get an InputStream for reading and decompressing the content
1370        InputStream data = compressedData.getInputStream();
1371        ByteArrayOutputStream os = new ByteArrayOutputStream();
1372        Util.copyStream(data, os, null);
1373      
1374        return os.toByteArray();
1375      }
1376    
1377    
1378      /**
1379       * Creates a CMS <code>CompressedData</code> object.
1380       * <p>
1381       *
1382       * @param message the message to be compressed, as byte representation
1383       *
1384       * @return the DER encoded <code>CompressedData</code>
1385       *
1386       * @throws CMSException if the <code>CompressedData</code> object cannot
1387       *                          be created
1388       * @throws IOException if an I/O error occurs
1389       * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1390       */
1391      public byte[] createCompressedData(byte[] message)
1392        throws CMSException, IOException, NoSuchAlgorithmException {
1393    
1394        System.out.println("Create a new CompressedData message");
1395    
1396        // create a new CompressedData object 
1397        CompressedData compressedData = new CompressedData(message, 
1398                                                          (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(),
1399                                                           CompressedData.IMPLICIT);
1400        ContentInfo ci = new ContentInfo(compressedData);
1401        return ci.getEncoded();
1402      }
1403    
1404      /**
1405       * Parses a CMS <code>CompressedData</code> object.
1406       *
1407       * @param encoding the DER encoded <code>CompressedData</code> object 
1408       *
1409       * @return the decompressed message as byte array
1410       *
1411       * @throws CMSException if the CompressedData cannot be parsed
1412       * @throws IOException if an I/O error occurs
1413       * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1414       */
1415      public byte[] getCompressedData(byte[] encoding) 
1416        throws CMSException, IOException, NoSuchAlgorithmException {
1417        
1418        System.out.println("Parse CompressedData message.");
1419        ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
1420        // create the CompressedData object
1421        CompressedData compressedData = new CompressedData(encodedStream);
1422        // decompress
1423        return compressedData.getContent();
1424      }
1425      
1426      /**
1427       * Starts the test.
1428       */
1429      public void start() {
1430        
1431        PrivateKey signerPrivateKey = null; 
1432        X509Certificate[] signerCertChain = null;
1433        PrivateKey recipient1PrivateKey = null; 
1434        X509Certificate recipient1Cert = null;
1435        PrivateKey recipient2PrivateKey = null; 
1436        X509Certificate recipient2Cert = null;
1437        
1438        // AES-GCM
1439    
1440        // AES128-GCM
1441        AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes128_GCM.clone();
1442        signerPrivateKey = ecdsa256bitSignerPrivateKey_;
1443        signerCertChain = ecdsa256bitSignerCertChain_;
1444        recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_; 
1445        recipient1Cert = ecdh256bitRecipient1Cert_;
1446        recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_; 
1447        recipient2Cert = ecdh256bitRecipient2Cert_;
1448        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1449        // AES192-GCM
1450        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes192_GCM.clone();
1451        signerPrivateKey = ecdsa384bitSignerPrivateKey_;
1452        signerCertChain = ecdsa384bitSignerCertChain_;
1453        recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_; 
1454        recipient1Cert = ecdh384bitRecipient1Cert_;
1455        recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_; 
1456        recipient2Cert = ecdh384bitRecipient2Cert_;
1457        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1458        // AES256-GCM
1459        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone();
1460        signerPrivateKey = ecdsa521bitSignerPrivateKey_;
1461        signerCertChain = ecdsa521bitSignerCertChain_;
1462        recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_; 
1463        recipient1Cert = ecdh521bitRecipient1Cert_;
1464        recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_; 
1465        recipient2Cert = ecdh521bitRecipient2Cert_;
1466        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1467        
1468        // AES-CBC-CMAC
1469        
1470        // AES-CBC-CMAC-128
1471        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_128.clone();
1472        signerPrivateKey = ecdsa256bitSignerPrivateKey_;
1473        signerCertChain = ecdsa256bitSignerCertChain_;
1474        recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_; 
1475        recipient1Cert = ecdh256bitRecipient1Cert_;
1476        recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_; 
1477        recipient2Cert = ecdh256bitRecipient2Cert_;
1478        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1479        // AES-CBC-CMAC-192
1480        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_192.clone();
1481        signerPrivateKey = ecdsa384bitSignerPrivateKey_;
1482        signerCertChain = ecdsa384bitSignerCertChain_;
1483        recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_; 
1484        recipient1Cert = ecdh384bitRecipient1Cert_;
1485        recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_; 
1486        recipient2Cert = ecdh384bitRecipient2Cert_;
1487        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1488        // AES-CBC-CMAC-256
1489        contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_256.clone();
1490        signerPrivateKey = ecdsa521bitSignerPrivateKey_;
1491        signerCertChain = ecdsa521bitSignerCertChain_;
1492        recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_; 
1493        recipient1Cert = ecdh521bitRecipient1Cert_;
1494        recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_; 
1495        recipient2Cert = ecdh521bitRecipient2Cert_;
1496        start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1497      }
1498      
1499      /**
1500       * Starts the test for the given content-authenticated encryption algorithm.
1501       * 
1502       * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
1503       */
1504      public void start(AlgorithmID contentAuthEncAlg,
1505                        PrivateKey signerPrivateKey, 
1506                        X509Certificate[] signerCertChain,
1507                        PrivateKey recipient1PrivateKey, 
1508                        X509Certificate recipient1Cert,
1509                        PrivateKey recipient2PrivateKey, 
1510                        X509Certificate recipient2Cert) {
1511         // the test message
1512        String m = "This is the test message.";
1513        System.out.println("Test message: \""+m+"\"");
1514        System.out.println();
1515        byte[] message = m.getBytes();
1516    
1517        try {
1518          byte[] encodedAuthEnvelopedData;
1519          byte[] encodedSignedAuthEnvelopedData;
1520          System.out.println("Stream implementation demos");
1521          System.out.println("===========================");
1522    
1523          AlgorithmID hashAlgorithm = AlgorithmID.sha256;
1524          AlgorithmID signatureAlgorithm = AlgorithmID.ecdsa_With_SHA256;
1525          
1526          
1527          // the stream implementation
1528          System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [create]:\n");
1529          // compress content
1530          byte[] compressedData = createCompressedDataStream(message);
1531          encodedAuthEnvelopedData = createAuthEnvelopedDataStream(compressedData, 
1532                                                                   (AlgorithmID)contentAuthEncAlg.clone(),
1533                                                                   recipient1Cert, 
1534                                                                   recipient2Cert);
1535          encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData, 
1536                                                                  SignedDataStream.IMPLICIT,
1537                                                                  (AlgorithmID)hashAlgorithm.clone(),
1538                                                                  (AlgorithmID)signatureAlgorithm.clone(),
1539                                                                  signerPrivateKey,
1540                                                                  signerCertChain);
1541          
1542          // transmit data
1543          System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n");
1544          // verify signature
1545          encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain);
1546          // parse contents
1547          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1548          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1549          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1550          parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1551          System.out.println("Decrypt for the several recipients using their certificate.");
1552          parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1553    
1554          // the non-stream implementation
1555          System.out.println("\nNon-stream implementation demos");
1556          System.out.println("===============================");
1557    
1558                
1559          System.out.println("\nCMS Signed AuthEnvelopedData demo [create]:\n");
1560          // compress content
1561          compressedData = createCompressedData(message);
1562          encodedAuthEnvelopedData = createAuthEnvelopedData(compressedData, 
1563                                                             (AlgorithmID)contentAuthEncAlg.clone(),
1564                                                             recipient1Cert, 
1565                                                             recipient2Cert);
1566          encodedSignedAuthEnvelopedData = createSignedData(encodedAuthEnvelopedData, 
1567                                                            SignedData.IMPLICIT,
1568                                                            (AlgorithmID)hashAlgorithm.clone(),
1569                                                            (AlgorithmID)signatureAlgorithm.clone(),
1570                                                            signerPrivateKey,
1571                                                            signerCertChain);
1572          // transmit data
1573          System.out.println("\nCMS Signed AuthEnvelopedData demo [parse]:\n");
1574          // verify signature
1575          encodedAuthEnvelopedData = getSignedData(encodedSignedAuthEnvelopedData, null, signerCertChain);
1576          // parse contents
1577          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1578          parseAuthEnvelopedDataWithRecipientInfoIndex(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1579          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1580          parseAuthEnvelopedDataWithRecipientIdentifier(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1581          System.out.println("Decrypt for the several recipients using their certificate.");
1582          parseAuthEnvelopedDataWithRecipientCert(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1583          
1584          
1585          System.out.println("OutputStream implementation demos");
1586          System.out.println("===========================");
1587    
1588          
1589          // the stream implementation
1590          System.out.println("\nCMS Signed AuthEnvelopedDataOutputStream demo [create]:\n");
1591          // compression of content is done in method createAuthEnvelopedDataOutputStream
1592          encodedAuthEnvelopedData = createAuthEnvelopedDataOutputStream(message, 
1593                                                                         (AlgorithmID)contentAuthEncAlg.clone(),                                                                     recipient1Cert, 
1594                                                                         recipient2Cert);
1595          encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData, 
1596                                                                  SignedDataStream.IMPLICIT,
1597                                                                  (AlgorithmID)hashAlgorithm.clone(),
1598                                                                  (AlgorithmID)signatureAlgorithm.clone(),
1599                                                                  signerPrivateKey,
1600                                                                  signerCertChain);
1601          
1602          // transmit data
1603          System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n");
1604          // verify signature
1605          encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain);
1606          // parse contents
1607          System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1608          parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1609          System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1610          parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1611          System.out.println("Decrypt for the several recipients using their certificate.");
1612          parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1613          
1614    
1615            } catch (Exception ex) {
1616              ex.printStackTrace();
1617              throw new RuntimeException(ex.toString());
1618            }
1619      }
1620      
1621      
1622      /**
1623       * Main method.
1624       *
1625       * @throws IOException
1626       *            if an I/O error occurs when reading required keys
1627       *            and certificates from files
1628       */
1629      public static void main(String argv[]) throws Exception {
1630    
1631        DemoUtil.initDemos();
1632        ECCDemoUtil.installIaikEccProvider();
1633    
1634        (new EckaEGAuthEnvelopedDataDemo()).start();
1635        System.out.println("\nReady!");
1636        DemoUtil.waitKey();
1637      }
1638    }