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