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/ECDHAuthEnvelopedDataDemo.java 5     12.02.25 17:58 Dbratko $
029// $Revision: 5 $
030//
031
032
033package demo.cms.ecc;
034
035import java.io.ByteArrayInputStream;
036import java.io.ByteArrayOutputStream;
037import java.io.IOException;
038import java.io.InputStream;
039import java.security.InvalidKeyException;
040import java.security.Key;
041import java.security.NoSuchAlgorithmException;
042
043import javax.crypto.SecretKey;
044
045import demo.DemoUtil;
046import iaik.asn1.ObjectID;
047import iaik.asn1.structures.AlgorithmID;
048import iaik.asn1.structures.Attribute;
049import iaik.cms.AuthEnvelopedData;
050import iaik.cms.AuthEnvelopedDataOutputStream;
051import iaik.cms.AuthEnvelopedDataStream;
052import iaik.cms.CMSException;
053import iaik.cms.ContentInfo;
054import iaik.cms.ContentInfoOutputStream;
055import iaik.cms.ContentInfoStream;
056import iaik.cms.EncryptedContentInfo;
057import iaik.cms.EncryptedContentInfoStream;
058import iaik.cms.IssuerAndSerialNumber;
059import iaik.cms.KeyIdentifier;
060import iaik.cms.OriginatorInfo;
061import iaik.cms.RecipientInfo;
062import iaik.cms.RecipientKeyIdentifier;
063import iaik.cms.attributes.CMSContentType;
064import iaik.utils.Util;
065import iaik.x509.X509Certificate;
066
067/**
068 * Demonstrates the usage of class {@link iaik.cms.AuthEnvelopedDataStream}, 
069 * {@link iaik.cms.AuthEnvelopedData} and {@link iaik.cms.AuthEnvelopedDataOutputStream}
070 * for authenticated encrypting data using the CMS type
071 * AuthEnvelopedData by using Static-Static ECDH according to <a href = 
072 * "http://www.ietf.org/rfc/rfc6278.txt" target="_blank">6278</a> as 
073 * key agreement method.
074 * <p>
075 * Any keys/certificates required for this demo are read from a keystore
076 * file "cmsecc.keystore" located in your current working directory. If
077 * the keystore file does not exist you can create it by running the
078 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
079 * program. 
080 * <p>
081 * Additionally to <code>iaik_cms.jar</code> you also must have 
082 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
083 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
084 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
085 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
086 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
087 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
088 * in your classpath.
089 *
090 * @see iaik.cms.AuthEnvelopedDataStream
091 * @see iaik.cms.AuthEnvelopedData
092 * @see iaik.cms.AuthEnvelopedDataOutputStream
093 * @see iaik.cms.RecipientInfo
094 * @see iaik.cms.KeyAgreeRecipientInfo
095 * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore
096 */
097public class ECDHAuthEnvelopedDataDemo extends StaticStaticECDHDemo {
098
099  
100
101  /**
102   * Setup the demo certificate chains.
103   *
104   * Keys and certificates are retrieved from the demo keyStore file
105   * "cmsecc.keystore" located in your current working directory. If
106   * the keystore file does not exist you can create it by running the
107   * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
108   * program. 
109   *
110   * @throws IOException if keys/certificates cannot be read from the keystore
111   */
112  public ECDHAuthEnvelopedDataDemo() throws IOException {
113    super();
114    System.out.println();
115    System.out.println("**********************************************************************************");
116    System.out.println("*                          ECDH AuthEnvelopedData demo                           *");
117    System.out.println("*   (shows the usage of the CMS AuthEnvelopedData type implementation for ECDH)  *");
118    System.out.println("**********************************************************************************");
119    System.out.println();
120    
121    
122  }
123
124  /**
125   * Creates a CMS <code>AuthEnvelopedDataStream</code> message.
126   *
127   * @param message the message to be authenticated-enveloped, as byte representation
128   * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
129   * 
130   * @return the encoding of the <code>AuthEnvelopedData</code> object just created
131   * 
132   * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
133   *                          be created
134   * @throws IOException if an I/O error occurs
135   */
136  public byte[] createAuthEnvelopedDataStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException {
137
138    // we are testing the stream interface
139    ByteArrayInputStream is = new ByteArrayInputStream(message);
140
141    AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is, contentAuthEncAlg);
142    if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
143        contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
144        contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
145      // for aes-ccm we need to know the data input length in advance
146      authEnvelopedData.setInputLength(message.length);
147    }
148    
149    //  static-static mode: set OriginatorInfo
150    OriginatorInfo originator = new OriginatorInfo();
151    originator.setCertificates(ecdhOriginatorCerts_);
152    authEnvelopedData.setOriginatorInfo(originator);
153    
154    //  create some authenticated attributes
155    try {
156      Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
157      authEnvelopedData.setAuthenticatedAttributes(attributes);
158    } catch (Exception ex) {
159      throw new CMSException("Error creating attribute: " + ex.toString());   
160    }
161   
162    // create the recipient infos
163    RecipientInfo[] recipients = createRecipients();
164    // specify the recipients of the encrypted message
165    authEnvelopedData.setRecipientInfos(recipients);
166
167    // return the EnvelopedDate as DER encoded byte array with block size 4
168    // (just for testing; in real application we will use a proper blocksize,
169    //  e.g. 2048, 4096,..)
170    authEnvelopedData.setBlockSize(4);
171    ByteArrayOutputStream os = new ByteArrayOutputStream();
172    ContentInfoStream cis = new ContentInfoStream(authEnvelopedData);
173    cis.writeTo(os);
174    return os.toByteArray();
175  }
176  
177  /**
178   * Creates a CMS <code>AuthEnvelopedData</code> message using the
179   * {@link iaik.cms.AuthEnvelopedDataOutputStream AuthEnvelopedDataOutputStream}
180   * class.
181   *
182   * @param message the message to be authenticated-enveloped, as byte representation
183   * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
184   * 
185   * @return the encoding of the <code>AuthEnvelopedData</code> object just created
186   * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
187   *                          be created
188   * @throws IOException if an I/O error occurs
189   */
190  public byte[] createAuthEnvelopedDataOutputStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException {
191
192    //  a stream from which to read the data to be encrypted
193    ByteArrayInputStream is = new ByteArrayInputStream(message);
194    
195    // the stream to which to write the AuthEnvelopedData
196    ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
197
198
199    //  wrap AuthEnvelopedData into a ContentInfo 
200    ContentInfoOutputStream contentInfoStream = 
201      new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
202    // create a new AuthEnvelopedData object encrypted with AES
203
204    AuthEnvelopedDataOutputStream  authEnvelopedData =
205        new AuthEnvelopedDataOutputStream(contentInfoStream, 
206                                          contentAuthEncAlg);
207  
208    if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
209        contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
210        contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
211      // for aes-ccm we need to know the data input length in advance
212      authEnvelopedData.setInputLength(message.length);
213    }
214    //  static-static mode: set OriginatorInfo
215    OriginatorInfo originator = new OriginatorInfo();
216    originator.setCertificates(ecdhOriginatorCerts_);
217    authEnvelopedData.setOriginatorInfo(originator);
218    
219    //  create some authenticated attributes
220    try {
221      Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
222      authEnvelopedData.setAuthenticatedAttributes(attributes);
223    } catch (Exception ex) {
224      throw new CMSException("Error creating attribute: " + ex.toString());   
225    }
226    
227    // create the recipient infos
228    RecipientInfo[] recipients = createRecipients();
229    /// specify the recipients of the encrypted message
230    authEnvelopedData.setRecipientInfos(recipients);
231
232    int blockSize = 4; // in real world we would use a block size like 2048
233    //  write in the data to be encrypted
234    byte[] buffer = new byte[blockSize];
235    int bytesRead;
236    while ((bytesRead = is.read(buffer)) != -1) {
237      authEnvelopedData.write(buffer, 0, bytesRead);
238    }
239    
240    // closing the stream finishes encryption and closes the underlying stream
241    authEnvelopedData.close();
242    return resultStream.toByteArray();
243  }
244
245  /**
246   * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
247   * the recipient identified by its index into the recipientInfos field.
248   *
249   * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
250   * @param key the key to decrypt the message
251   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
252   *                           to which the specified key belongs
253   *
254   * @return the recovered message, as byte array
255   * @throws CMSException if the message cannot be recovered
256   * @throws IOException if a stream read/write error occurs
257   */
258  public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
259    throws CMSException, IOException {
260
261    // create the AuthEnvelopedData object from a DER encoded byte array
262    // we are testing the stream interface
263    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
264    AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
265
266    System.out.println("Information about the encrypted data:");
267    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
268    System.out.println("Content type: "+eci.getContentType().getName());
269    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
270
271    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
272    RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
273    
274    // for demonstration purposes we only look one time for all recipients included:
275    if (recipientInfoIndex == 0) {
276      int k = 0;
277      for (int i=0; i<recipients.length; i++) {
278        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
279        for (int j = 0; j < recipientIDs.length; j++) {
280          System.out.println("Recipient "+(++k)+":");
281          System.out.println(recipientIDs[j]);
282        }   
283      }
284    }
285    // decrypt the message for the first recipient
286    try {
287      authEnvelopedData.setupCipher(key, recipientInfoIndex);
288      InputStream decrypted = authEnvelopedData.getInputStream();
289      ByteArrayOutputStream os = new ByteArrayOutputStream();
290      Util.copyStream(decrypted, os, null);
291
292      return os.toByteArray();
293
294    } catch (InvalidKeyException ex) {
295      throw new CMSException("Private key error: "+ex.getMessage());
296    } catch (NoSuchAlgorithmException ex) {
297      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
298    }
299  }
300  
301  /**
302   * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
303   * the recipient identified by recipient identifier.
304   *
305   * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
306   * @param key the key to decrypt the message
307   * @param recipientID the recipient identifier uniquely identifying the key of the
308   *        recipient
309   *
310   * @return the recovered message, as byte array
311   * @throws CMSException if the message cannot be recovered
312   * @throws IOException if a stream read/write error occurs
313   */
314  public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
315    throws CMSException, IOException {
316
317    // create the AuthEnvelopedData object from a DER encoded byte array
318    // we are testing the stream interface
319    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
320    AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
321
322    System.out.println("Information about the encrypted data:");
323    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
324    System.out.println("Content type: "+eci.getContentType().getName());
325    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
326    
327    // get the right RecipientInfo
328    System.out.println("\nSearch for RecipientInfo:");
329    RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
330    if (recipient != null) {
331      System.out.println("RecipientInfo: " + recipient);   
332    } else {
333      throw new CMSException("No recipient with ID: " + recipientID);
334    }    
335    // decrypt the content encryption key and the content
336    try {
337      System.out.println("Decrypt encrypted content encryption key...");
338      SecretKey cek = recipient.decryptKey(key, recipientID);
339      System.out.println("Decrypt content with decrypted content encryption key...");
340      authEnvelopedData.setupCipher(cek);
341      InputStream decrypted = authEnvelopedData.getInputStream();
342      ByteArrayOutputStream os = new ByteArrayOutputStream();
343      Util.copyStream(decrypted, os, null);
344
345      return os.toByteArray();
346
347    } catch (InvalidKeyException ex) {
348      throw new CMSException("Private key error: "+ex.getMessage());
349    } catch (NoSuchAlgorithmException ex) {
350      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
351    }
352  }
353  
354  // non stream
355
356  /**
357   * Creates a CMS <code>AuthEnvelopedData</code> message.
358   * 
359   * @param message the message to be authenticated-enveloped, as byte representation
360   * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
361   * 
362   * @return the encoded <code>AuthEnvelopedData</code>, as byte array
363   * 
364   * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
365   *                          be created
366   */
367  public byte[] createAuthEnvelopedData(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException {
368    
369    AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(message, contentAuthEncAlg);
370    
371    if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 
372        contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
373        contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
374      // for aes-ccm we need to know the data input length in advance
375      authEnvelopedData.setInputLength(message.length);
376    }
377    
378    //  static-static mode: set OriginatorInfo
379    OriginatorInfo originator = new OriginatorInfo();
380    originator.setCertificates(ecdhOriginatorCerts_);
381    authEnvelopedData.setOriginatorInfo(originator);
382    
383    //  create some authenticated attributes
384    try {
385      Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
386      authEnvelopedData.setAuthenticatedAttributes(attributes);
387    } catch (Exception ex) {
388      throw new CMSException("Error creating attribute: " + ex.toString());   
389    }
390     
391    // set the RecipientInfos
392    RecipientInfo[] recipients = createRecipients();
393    authEnvelopedData.setRecipientInfos(recipients);
394
395    // return encoded AuthEnvelopedData
396    // wrap into contentInfo
397    ContentInfo ci = new ContentInfo(authEnvelopedData);
398    return ci.getEncoded();
399  }
400
401
402  /**
403   * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
404   * the recipient identified by its index into the recipientInfos field.
405   *
406   * @param enc the encoded <code>AuthEnvelopedData</code>
407   * @param key the key to decrypt the message
408   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
409   *                           to which the specified key belongs
410   *
411   * @return the recovered message, as byte array
412   * 
413   * @throws CMSException if the message cannot be recovered
414   * @throws IOException if an I/O error occurs
415   */
416  public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
417    throws CMSException, IOException {
418    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
419    AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
420
421    System.out.println("Information about the encrypted data:");
422    EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
423    System.out.println("Content type: "+eci.getContentType().getName());
424    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
425
426    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
427    RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
428    
429    // for demonstration purposes we only look one time for all recipients included:
430    if (recipientInfoIndex == 0) {
431      int k = 0;
432      for (int i=0; i<recipients.length; i++) {
433        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
434        for (int j = 0; j < recipientIDs.length; j++) {
435          System.out.println("Recipient "+(++k)+":");
436          System.out.println(recipientIDs[j]);
437        }   
438      }
439    }
440    
441    // decrypt the message
442    try {
443      authEnvelopedData.setupCipher(key, recipientInfoIndex);
444      return authEnvelopedData.getContent();
445
446    } catch (InvalidKeyException ex) {
447      throw new CMSException("Private key error: "+ex.getMessage());
448    } catch (NoSuchAlgorithmException ex) {
449      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
450    }
451  }
452  
453  /**
454   * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
455   * the recipient identified by recipient identifier.
456   * <p>
457   * This way of decrypting the content may be used for any type of RecipientInfo
458   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
459   * recipient in mind is identified by its recipient identifier.
460   *
461   * @param enc the encoded <code>AuthenticatedData</code>
462   * @param key the key to decrypt the message
463   * @param recipientID the recipient identifier uniquely identifying the key of the
464   *        recipient
465   *
466   * @return the recovered message, as byte array
467   * @throws CMSException if the message cannot be recovered
468   * @throws IOException if an I/O error occurs
469   */
470  public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
471    throws CMSException, IOException {
472    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
473    AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
474
475    System.out.println("Information about the encrypted data:");
476    EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
477    System.out.println("Content type: "+eci.getContentType().getName());
478    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
479
480    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
481
482    // get the right RecipientInfo
483    System.out.println("\nSearch for RecipientInfo:");
484    RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
485    if (recipient != null) {
486      System.out.println("RecipientInfo: " + recipient);   
487    } else {
488      throw new CMSException("No recipient with ID: " + recipientID);
489    }    
490    // decrypt the content encryption key and the content
491    try {
492      System.out.println("Decrypt encrypted content encryption key...");
493      SecretKey cek = recipient.decryptKey(key, recipientID);
494      System.out.println("Decrypt content with decrypted content encryption key...");
495      authEnvelopedData.setupCipher(cek);
496      return authEnvelopedData.getContent();
497
498    } catch (InvalidKeyException ex) {
499      throw new CMSException("Private key error: "+ex.getMessage());
500    } catch (NoSuchAlgorithmException ex) {
501      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
502    }
503  }
504  
505  /**
506   * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
507   * the recipient identified by its recipient certificate.
508   *
509   * @param enc the encoded <code>AuthEnvelopedData</code>
510   * @param key the key to decrypt the message
511   * @param recipientCert the certificate of the recipient 
512   *
513   * @return the recovered message, as byte array
514   *
515   * @throws CMSException if the message cannot be recovered
516   */
517  public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 
518    throws CMSException, IOException {
519    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
520    AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
521
522    System.out.println("Information about the encrypted data:");
523    EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
524    System.out.println("Content type: "+eci.getContentType().getName());
525    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
526
527    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
528
529    // decrypt the content encryption key and the content
530    try {
531      System.out.println("Decrypt the content...");
532      authEnvelopedData.setupCipher(key, recipientCert);
533      return authEnvelopedData.getContent();
534
535    } catch (InvalidKeyException ex) {
536      throw new CMSException("Private key error: "+ex.getMessage());
537    } catch (NoSuchAlgorithmException ex) {
538      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
539    }
540  }
541  
542  
543  /**
544   * Parses an AuthEnvelopedData and decrypts the content for all test recipients
545   * using the index into the recipientInfos field for identifying the recipient.
546   *
547   * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
548   * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
549   *
550   * @throws Exception if some error occurs during decoding/decryption
551   */ 
552  public void parseAuthEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
553    byte[] receivedMessage;
554    if (stream) {
555      // ecdhUser1
556      System.out.println("\nDecrypt for ecdhUser1:");
557      receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, 0);
558      System.out.print("\nDecrypted content: ");
559      System.out.println(new String(receivedMessage));
560      // ecdhUser2
561      System.out.println("\nDecrypt for ecdhUser2:");
562      receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, 0);
563      System.out.print("\nDecrypted content: ");
564      System.out.println(new String(receivedMessage));
565    } else {
566      // ecdhUser1
567      System.out.println("\nDecrypt for ecdhUser1:");
568      receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, 0);
569      System.out.print("\nDecrypted content: ");
570      System.out.println(new String(receivedMessage));
571      // ecdhUser2
572      System.out.println("\nDecrypt for ecdhUser2:");
573      receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, 0);
574      System.out.print("\nDecrypted content: ");
575      System.out.println(new String(receivedMessage));
576    }    
577  }
578  
579  /**
580   * Parses an AuthEnvelopedData and decrypts the content for all test recipients
581   * using their recipient identifiers for identifying the recipient.
582   *
583   * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
584   * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 
585   *
586   * @throws Exception if some error occurs during decoding/decryption
587   */ 
588  public void parseAuthEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
589    byte[] receivedMessage;
590    if (stream) {
591      // ecdhUser1
592      System.out.println("\nDecrypt for ecdhUser1:");
593      receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
594      System.out.print("\nDecrypted content: ");
595      System.out.println(new String(receivedMessage));
596      // ecdhUser2
597      System.out.println("\nDecrypt for ecdhUser2:");
598      receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
599      System.out.print("\nDecrypted content: ");
600      System.out.println(new String(receivedMessage));
601    } else {
602      // ecdhUser1
603      System.out.println("\nDecrypt for ecdhUser1:");
604      receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_));
605      System.out.print("\nDecrypted content: ");
606      System.out.println(new String(receivedMessage));
607      // ecdhUser2
608      System.out.println("\nDecrypt for ecdhUser2:");
609      receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_));
610      System.out.print("\nDecrypted content: ");
611      System.out.println(new String(receivedMessage));
612    }    
613  }
614  
615  
616  
617  /**
618   * Starts the demo.
619   */
620  public void start() {
621   
622    // AES-GCM
623    AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone();
624    start(contentAuthEncAlg);
625    
626    // AES-CCM
627    contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_CCM.clone();
628    start(contentAuthEncAlg);
629    
630    
631    // ChaCha20Poly1305
632    contentAuthEncAlg = (AlgorithmID)AlgorithmID.chacha20Poly1305.clone();
633    start(contentAuthEncAlg);
634  }
635  
636  /**
637   * Starts the demo for the given content-authenticated encryption algorithm.
638   * 
639   * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
640   */
641  public void start(AlgorithmID contentAuthEncAlg) {
642     // the test message
643    String m = "This is the test message.";
644    System.out.println("Test message: \""+m+"\"");
645    System.out.println();
646    byte[] message = m.getBytes();
647
648    try {
649      byte[] encoding;
650      System.out.println("Stream implementation demos");
651      System.out.println("===========================");
652
653
654      // the stream implementation
655      //
656      // test CMS AuthEnvelopedDataStream
657      //
658      System.out.println("\nCMS AuthEnvelopedDataStream demo [create]:\n");
659      encoding = createAuthEnvelopedDataStream(message, (AlgorithmID)contentAuthEncAlg.clone());
660      // transmit data
661      System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
662      System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
663      parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding);
664      System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
665      parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding);
666      
667      
668      System.out.println("\nOutputStream implementation demos");
669      System.out.println("=================================");
670
671
672      // the output stream implementation
673      //
674      // test CMS AuthEnvelopedDataOutputStream
675      //
676      System.out.println("\nCMS AuthEnvelopedDataOutputStream demo [create]:\n");
677      encoding = createAuthEnvelopedDataOutputStream(message, (AlgorithmID)contentAuthEncAlg.clone());
678      // transmit data
679      System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
680      System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
681      parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding);
682      System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
683      parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding);
684      
685      // the non-stream implementation
686      System.out.println("\nNon-stream implementation demos");
687      System.out.println("===============================");
688
689            
690      //
691      // test CMS AuthEnvelopedData
692      //
693      System.out.println("\nCMS AuthEnvelopedData demo [create]:\n");
694      encoding = createAuthEnvelopedData(message, (AlgorithmID)contentAuthEncAlg.clone());
695      // transmit data
696      System.out.println("\nCMS AuthEnvelopedData demo [parse]:\n");
697      System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
698      parseAuthEnvelopedDataWithRecipientInfoIndex(false, encoding);
699      System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
700      parseAuthEnvelopedDataWithRecipientIdentifier(false, encoding);
701      
702      
703
704    } catch (Exception ex) {
705      ex.printStackTrace();
706      throw new RuntimeException(ex.toString());
707    }
708  }
709  
710  /**
711   * Main method.
712   *
713   * @throws IOException
714   *            if an I/O error occurs when reading required keys
715   *            and certificates from the keystore file
716   */
717  public static void main(String argv[]) throws Exception {
718
719    DemoUtil.initDemos();
720    ECCDemoUtil.installIaikEccProvider();
721    (new ECDHAuthEnvelopedDataDemo()).start();
722    System.out.println("\nReady!");
723    System.in.read();
724  }
725}