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/envelopedData/EnvelopedDataDemo.java 40    12.02.25 17:58 Dbratko $
029// $Revision: 40 $
030//
031
032
033package demo.cms.envelopedData;
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;
042import java.security.PrivateKey;
043import java.security.SecureRandom;
044import java.util.Arrays;
045
046import javax.crypto.KeyGenerator;
047import javax.crypto.SecretKey;
048
049import demo.DemoUtil;
050import demo.keystore.CMSKeyStore;
051import iaik.asn1.structures.AlgorithmID;
052import iaik.cms.CMSException;
053import iaik.cms.CertificateIdentifier;
054import iaik.cms.EncryptedContentInfo;
055import iaik.cms.EncryptedContentInfoStream;
056import iaik.cms.EnvelopedData;
057import iaik.cms.EnvelopedDataStream;
058import iaik.cms.IssuerAndSerialNumber;
059import iaik.cms.KEKIdentifier;
060import iaik.cms.KEKRecipientInfo;
061import iaik.cms.KeyAgreeRecipientInfo;
062import iaik.cms.KeyIdentifier;
063import iaik.cms.KeyTransRecipientInfo;
064import iaik.cms.RecipientInfo;
065import iaik.cms.RecipientKeyIdentifier;
066import iaik.cms.SecurityProvider;
067import iaik.cms.SubjectKeyID;
068import iaik.security.random.SecRandom;
069import iaik.utils.Util;
070import iaik.x509.X509Certificate;
071
072
073/**
074 * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataStream} and
075 * {@link iaik.cms.EnvelopedData} for encrypting data using the CMS type
076 * EnvelopedData.
077 * <p>
078 * This demo creates an EnvelopedData object and subsequently shows several
079 * ways that may be used for decrypting the content for some particular 
080 * recipient.
081 * <p>
082 * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore") 
083 * which has to be located in your current working directory and may be
084 * created by running the {@link demo.keystore.SetupCMSKeyStore
085 * SetupCMSKeyStore} program.
086 * <p>
087 * This demo uses TripleDES which has been deprecated by S/MIMEv4 (RFC 8551),
088 * see {@link AESEnvelopedDataDemo AESEnvelopedDataDemo} for an AES based demo.
089 * 
090 * @see iaik.cms.EnvelopedDataStream
091 * @see iaik.cms.EnvelopedData
092 * @see iaik.cms.RecipientInfo
093 * @see iaik.cms.KeyTransRecipientInfo
094 * @see iaik.cms.KeyAgreeRecipientInfo
095 * @see iaik.cms.KEKRecipientInfo
096 */
097public class EnvelopedDataDemo {
098
099  // certificate of rsaUser 1
100  X509Certificate rsaUser1_;
101  // private key of rsaUser 1
102  PrivateKey rsaUser1Pk_;
103  // certificate of rsaUser 2
104  X509Certificate rsaUser2_;
105  // private key of rsaUser 2
106  PrivateKey rsaUser2Pk_;
107
108  // certificate of esdhUser 1
109  X509Certificate esdhUser1_;
110  // private key of esdhUser 1
111  PrivateKey esdhUser1Pk_;
112  // certificate of esdhUser 2
113  X509Certificate esdhUser2_;
114  // private key of esdhUser 2
115  PrivateKey esdhUser2Pk_;
116  
117  // key encryption key for KEKRecipientInfo
118  SecretKey kek_;
119  byte[] kekID_;
120  
121  // content encryption algorithm to be used
122  AlgorithmID contentEncAlg_;
123  // cek algorithm
124  String cekAlg_;
125  // key wrap algorithm to be used
126  AlgorithmID keyWrapAlg_;
127  // key length (same for content encryption key and key encryption key
128  int keyLength_;
129
130  // secure random number generator
131  SecureRandom random_;
132  
133  /**
134   * Creates an EnvelopedDataDemo and setups the demo certificates.
135   * <br>
136   * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore")
137   * file which has to be located in your current working directory and may be
138   * created by running {@link demo.keystore.SetupCMSKeyStore
139   * SetupCMSKeyStore}.
140   * <br>
141   * TripleDES and TripleDES KeyWrap are used for content encryption and
142   * content encryption key wrapping.
143   *
144   * @throws IOException if an file read error occurs
145   * @throws NoSuchAlgorithmException if the requested TripleDES or TripleDES KeyWrap 
146   *                                     algorithms are not supported
147   */
148  public EnvelopedDataDemo() throws IOException, NoSuchAlgorithmException {
149    this((AlgorithmID)AlgorithmID.des_EDE3_CBC.clone(),
150         (AlgorithmID)AlgorithmID.cms_3DES_wrap.clone(),
151         (AlgorithmID)AlgorithmID.cms_3DES_wrap.clone(),
152         192);
153  }
154  
155  /**
156   * Creates an EnvelopedDataDemo and setups the demo certificates.
157   * <br>
158   * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore")
159   * file which has to be located in your current working directory and may be
160   * created by running {@link demo.keystore.SetupCMSKeyStore
161   * SetupCMSKeyStore}.
162   *
163   * @param contentEncAlg the content encryption algorithm to be used
164   * @param keyWrapAlg the key wrap algorithm to be used for wrapping the content 
165   *                   encryption key (for KeyAgreeRecipientInfos)
166   * @param keyLength the key length to be used (same for content encryption key
167   *                  and key encryption key) (for KeyAgreeRecipientInfos and
168   *                  KEKRecipientInfos)
169   *                                    
170   * @throws IOException if an file read error occurs
171   * @throws NoSuchAlgorithmException if the requested algorithms are not supported
172   */
173  public EnvelopedDataDemo(AlgorithmID contentEncAlg,
174                           AlgorithmID keyWrapAlg,
175                           int keyLength) throws IOException, NoSuchAlgorithmException {
176    this(contentEncAlg, keyWrapAlg, keyWrapAlg, keyLength);
177    
178  }
179      
180
181  /**
182   * Creates an EnvelopedDataDemo and setups the demo certificates.
183   * <br>
184   * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore")
185   * file which has to be located in your current working directory and may be
186   * created by running {@link demo.keystore.SetupCMSKeyStore
187   * SetupCMSKeyStore}.
188   *
189   * @param contentEncAlg the content encryption algorithm to be used
190   * @param keyWrapAlg the key wrap algorithm to be used for wrapping the content 
191   *                   encryption key (for KeyAgreeRecipientInfos)
192   * @param kekAlg the name of the key encryption key algorithm to be used
193   *               (for KEKRecipientInfos)
194   * @param keyLength the key length to be used (same for content encryption key
195   *                  and key encryption key) (for KeyAgreeRecipientInfos and
196   *                  KEKRecipientInfos)
197   *                                    
198   * @throws IOException if an file read error occurs
199   * @throws NoSuchAlgorithmException if the requested algorithms are not supported
200   */
201  public EnvelopedDataDemo(AlgorithmID contentEncAlg,
202                           AlgorithmID keyWrapAlg,
203                           AlgorithmID kekAlg,
204                           int keyLength) throws IOException, NoSuchAlgorithmException {
205    
206    System.out.println();
207    System.out.println("**********************************************************************************");
208    System.out.println("                  EnvelopedDataDemo " + contentEncAlg.getName());
209    System.out.println("        (shows the usage of the CMS EnvelopedData type implementation)          ");
210    System.out.println("**********************************************************************************");
211    System.out.println();
212    
213    // add all certificates to the list
214    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
215    rsaUser1_ = certs[0];
216    rsaUser1Pk_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
217    rsaUser2_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
218    rsaUser2Pk_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
219    
220    esdhUser1_ = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1)[0];
221    esdhUser1Pk_ = CMSKeyStore.getPrivateKey(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1);
222    esdhUser2_ = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_2)[0];
223    esdhUser2Pk_ = CMSKeyStore.getPrivateKey(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_2);
224    random_ = SecRandom.getDefault();
225    
226    contentEncAlg_ = contentEncAlg;
227    keyWrapAlg_ = keyWrapAlg;
228    keyLength_ = keyLength;
229    
230    // create a secret key encryption key for a KEKRecipientInfo
231    KeyGenerator kg = SecurityProvider.getSecurityProvider().getKeyGenerator(kekAlg, keyLength_);
232    kek_ = kg.generateKey();
233    kekID_ = new byte[] { 00, 00, 00, 01 };
234  }
235
236
237  /**
238   * Creates a CMS <code>EnvelopedDataStream</code> message.
239   *
240   * @param message the message to be enveloped, as byte representation
241   * @return the DER encoding of the <code>EnvelopedData</code> object just created
242   * @throws CMSException if the <code>EnvelopedData</code> object cannot
243   *                          be created
244   * @throws IOException if an I/O error occurs
245   */
246  public byte[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException {
247
248    EnvelopedDataStream enveloped_data;
249
250    // we are testing the stream interface
251    ByteArrayInputStream is = new ByteArrayInputStream(message);
252    // create a new EnvelopedData object encrypted with TripleDES CBC
253    try {
254      enveloped_data = new EnvelopedDataStream(is, (AlgorithmID)contentEncAlg_.clone());
255    } catch (NoSuchAlgorithmException ex) {
256      throw new CMSException("No implementation for content encryption algorithm: " + ex.toString());
257    }
258
259    // create the recipient infos
260    RecipientInfo[] recipients = createRecipients();
261    // specify the recipients of the encrypted message
262    enveloped_data.setRecipientInfos(recipients);
263
264    // return the EnvelopedDate as DER encoded byte array with block size 2048
265    ByteArrayOutputStream os = new ByteArrayOutputStream();
266    enveloped_data.writeTo(os, 2048);
267    return os.toByteArray();
268  }
269
270  /**
271   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
272   * the recipient identified by its index into the recipientInfos field.
273   * <p>
274   * This way of decrypting the content may be used for any type of RecipientInfo
275   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
276   * know at what index of the recipientInfo field the RecipientInfo for the 
277   * particular recipient in mind can be found. If the recipient in mind uses
278   * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
279   * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
280   * keys for more than only one recipient; since the recipientInfoIndex only
281   * specifies the RecipientInfo but not the encrypted content encryption key 
282   * -- if there are more than only one -- repeated decryption runs may be
283   * required as long as the decryption process completes successfully.
284   *
285   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
286   * @param key the key to decrypt the message
287   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
288   *                        to which the specified key belongs
289   *
290   * @return the recovered message, as byte array
291   * @throws CMSException if the message cannot be recovered
292   * @throws IOException if a stream read/write error occurs
293   */
294  public byte[] getEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
295    throws CMSException, IOException {
296
297    // create the EnvelopedData object from a DER encoded byte array
298    // we are testing the stream interface
299    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
300    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
301
302    System.out.println("Information about the encrypted data:");
303    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
304    System.out.println("Content type: "+eci.getContentType().getName());
305    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
306
307    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
308    RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
309    
310    // for demonstration purposes we only look one time for all recipients included:
311    if (recipientInfoIndex == 0) {
312      int k = 0;
313      for (int i=0; i<recipients.length; i++) {
314        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
315        for (int j = 0; j < recipientIDs.length; j++) {
316          System.out.println("Recipient "+(++k)+":");
317          System.out.println(recipientIDs[j]);
318        }   
319      }
320    }
321    // decrypt the message for the first recipient
322    try {
323      enveloped_data.setupCipher(key, recipientInfoIndex);
324      InputStream decrypted = enveloped_data.getInputStream();
325      ByteArrayOutputStream os = new ByteArrayOutputStream();
326      Util.copyStream(decrypted, os, null);
327
328      return os.toByteArray();
329
330    } catch (InvalidKeyException ex) {
331      throw new CMSException("Private key error: "+ex.getMessage());
332    } catch (NoSuchAlgorithmException ex) {
333      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
334    }
335  }
336  
337  /**
338   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
339   * the recipient identified by recipient identifier.
340   * <p>
341   * This way of decrypting the content may be used for any type of RecipientInfo
342   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
343   * recipient in mind is identified by its recipient identifier.
344   *
345   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
346   * @param key the key to decrypt the message
347   * @param recipientID the recipient identifier uniquely identifying the key of the
348   *        recipient
349   *
350   * @return the recovered message, as byte array
351   * @throws CMSException if the message cannot be recovered
352   * @throws IOException if a stream read/write error occurs
353   */
354  public byte[] getEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
355    throws CMSException, IOException {
356
357    // create the EnvelopedData object from a DER encoded byte array
358    // we are testing the stream interface
359    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
360    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
361
362    System.out.println("Information about the encrypted data:");
363    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
364    System.out.println("Content type: "+eci.getContentType().getName());
365    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
366    
367    // get the right RecipientInfo
368    System.out.println("\nSearch for RecipientInfo:");
369    RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
370    if (recipient != null) {
371      System.out.println("RecipientInfo: " + recipient);   
372    } else {
373      throw new CMSException("No recipient with ID: " + recipientID);
374    }    
375    // decrypt the content encryption key and the content
376    try {
377      System.out.println("Decrypt encrypted content encryption key...");
378      SecretKey cek = recipient.decryptKey(key, recipientID);
379      System.out.println("Decrypt content with decrypted content encryption key...");
380      enveloped_data.setupCipher(cek);
381      InputStream decrypted = enveloped_data.getInputStream();
382      ByteArrayOutputStream os = new ByteArrayOutputStream();
383      Util.copyStream(decrypted, os, null);
384
385      return os.toByteArray();
386
387    } catch (InvalidKeyException ex) {
388      throw new CMSException("Private key error: "+ex.getMessage());
389    } catch (NoSuchAlgorithmException ex) {
390      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
391    }
392  }
393  
394  /**
395   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
396   * the recipient identified by its recipient certificate or kekID.
397   * <p>
398   * Since recipient certificates only may be used for for RecipientInfos of type
399   * KeyTransRecipientInfo or KeyAgreeRecipientInfo, a key id has to be supplied
400   * for decrypting the content for a recipient using a KEKRecipientInfo.
401   *
402   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
403   * @param key the key to decrypt the message
404   * @param recipientCert the certificate of the recipient having a RecipientInfo of
405   *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
406   * @param kekID the kekID identifying the recipient key when using a RecipientInfo
407   *              of type KEKRecipientInfo
408   *
409   * @return the recovered message, as byte array
410   * @throws CMSException if the message cannot be recovered
411   * @throws IOException if a stream read/write error occurs
412   */
413  public byte[] getEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert, byte[] kekID)
414    throws CMSException, IOException {
415
416    // create the EnvelopedData object from a DER encoded byte array
417    // we are testing the stream interface
418    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
419    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
420
421    System.out.println("Information about the encrypted data:");
422    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
423    System.out.println("Content type: "+eci.getContentType().getName());
424    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
425    
426    // decrypt the content encryption key and the content
427    try {
428      System.out.println("Decrypt the content...");
429      if (recipientCert != null) {
430        enveloped_data.setupCipher(key, recipientCert);
431      } else {
432        // KEKRecipientInfo
433        enveloped_data.setupCipher(key, new KEKIdentifier(kekID));
434      }  
435      InputStream decrypted = enveloped_data.getInputStream();
436      ByteArrayOutputStream os = new ByteArrayOutputStream();
437      Util.copyStream(decrypted, os, null);
438
439      return os.toByteArray();
440
441    } catch (InvalidKeyException ex) {
442      throw new CMSException("Private key error: "+ex.getMessage());
443    } catch (NoSuchAlgorithmException ex) {
444      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
445    }
446  }
447
448
449  // non stream
450
451  /**
452   * Creates a CMS <code>EnvelopedData</code> message.
453   * 
454   * @param message the message to be enveloped, as byte representation
455   * 
456   * @return the encoded <code>EnvelopedData</code>, as byte array
457   * 
458   * @throws CMSException if the <code>EnvelopedData</code> object cannot
459   *                          be created
460   */
461  public byte[] createEnvelopedData(byte[] message) throws CMSException {
462    
463    EnvelopedData enveloped_data;
464
465    // create a new EnvelopedData object encrypted with TripleDES CBC
466    try {
467      enveloped_data = new EnvelopedData(message, (AlgorithmID)contentEncAlg_.clone());
468    } catch (NoSuchAlgorithmException ex) {
469      throw new CMSException("No implementation for content encryption algorithm: " + ex.toString());
470    }
471    
472    // set the RecipientInfos
473    RecipientInfo[] recipients = createRecipients();
474    enveloped_data.setRecipientInfos(recipients);
475
476    // return encoded EnvelopedData
477    return enveloped_data.getEncoded();
478  }
479
480
481  /**
482   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
483   * the recipient identified by its index into the recipientInfos field.
484   * <p>
485   * This way of decrypting the content may be used for any type of RecipientInfo
486   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
487   * know at what index of the recipientInfo field the RecipientInfo for the 
488   * particular recipient in mind can be found. If the recipient in mind uses
489   * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
490   * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
491   * keys for more than only one recipient; since the recipientInfoIndex only
492   * specifies the RecipientInfo but not the encrypted content encryption key 
493   * -- if there are more than only one -- repeated decryption runs may be
494   * required as long as the decryption process completes successfully.
495   *
496   * @param enc the encoded <code>EnvelopedData</code>
497   * 
498   * @param key the key to decrypt the message
499   * 
500   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
501   *                    to which the specified key belongs
502   *
503   * @return the recovered message, as byte array
504   * 
505   * @throws CMSException if the message cannot be recovered
506   * @throws IOException if an I/O error occurs
507   */
508  public byte[] getEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 
509    throws CMSException, IOException {
510    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
511    EnvelopedData enveloped_data = new EnvelopedData(bais);
512
513    System.out.println("Information about the encrypted data:");
514    EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
515    System.out.println("Content type: "+eci.getContentType().getName());
516    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
517
518    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
519    RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
520    
521    // for demonstration purposes we only look one time for all recipients included:
522    if (recipientInfoIndex == 0) {
523      int k = 0;
524      for (int i=0; i<recipients.length; i++) {
525        KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
526        for (int j = 0; j < recipientIDs.length; j++) {
527          System.out.println("Recipient "+(++k)+":");
528          System.out.println(recipientIDs[j]);
529        }   
530      }
531    }
532    
533    // decrypt the message
534    try {
535      enveloped_data.setupCipher(key, recipientInfoIndex);
536      return enveloped_data.getContent();
537
538    } catch (InvalidKeyException ex) {
539      throw new CMSException("Private key error: "+ex.getMessage());
540    } catch (NoSuchAlgorithmException ex) {
541      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
542    }
543  }
544  
545  /**
546   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
547   * the recipient identified by recipient identifier.
548   * <p>
549   * This way of decrypting the content may be used for any type of RecipientInfo
550   * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 
551   * recipient in mind is identified by its recipient identifier.
552   *
553   * @param enc the DER encoded <code>EnvelopedData</code> ASN.1 object
554   * @param key the key to decrypt the message
555   * @param recipientID the recipient identifier uniquely identifying the key of the
556   *        recipient
557   *
558   * @return the recovered message, as byte array
559   * @throws CMSException if the message cannot be recovered
560   * @throws IOException if an I/O error occurs
561   */
562  public byte[] getEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 
563    throws CMSException, IOException {
564    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
565    EnvelopedData enveloped_data = new EnvelopedData(bais);
566
567    System.out.println("Information about the encrypted data:");
568    EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
569    System.out.println("Content type: "+eci.getContentType().getName());
570    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
571
572    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
573
574    // get the right RecipientInfo
575    System.out.println("\nSearch for RecipientInfo:");
576    RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
577    if (recipient != null) {
578      System.out.println("RecipientInfo: " + recipient);   
579    } else {
580      throw new CMSException("No recipient with ID: " + recipientID);
581    }    
582    // decrypt the content encryption key and the content
583    try {
584      System.out.println("Decrypt encrypted content encryption key...");
585      SecretKey cek = recipient.decryptKey(key, recipientID);
586      System.out.println("Decrypt content with decrypted content encryption key...");
587      enveloped_data.setupCipher(cek);
588      return enveloped_data.getContent();
589
590    } catch (InvalidKeyException ex) {
591      throw new CMSException("Private key error: "+ex.getMessage());
592    } catch (NoSuchAlgorithmException ex) {
593      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
594    }
595  }
596  
597  /**
598   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
599   * the recipient identified by its recipient certificate or keyID.
600   * <p>
601   * Since recipient certificates only may be used for for RecipientInfos of type
602   * KeyTransRecipientInfo or KeyAgreeRecipientInfo, a key id has to be supplied
603   * for decrypting the content for a recipient using a KEKRecipientInfo.
604   *
605   * @param enc the DER encoded <code>EnvelopedData</code> ASN.1 object
606   * @param key the key to decrypt the message
607   * @param recipientCert the certificate of the recipient having a RecipientInfo of
608   *                      type KeyTransRecipientInfo or KeyAgreeRecipientInfo
609   * @param kekID the kekID identifying the recipient key when using a RecipientInfo
610   *              of type KEKRecipientInfo
611   *
612   * @return the recovered message, as byte array
613   * @throws CMSException if the message cannot be recovered
614   */
615  public byte[] getEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert, byte[] kekID) 
616    throws CMSException, IOException {
617    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
618    EnvelopedData enveloped_data = new EnvelopedData(bais);
619
620    System.out.println("Information about the encrypted data:");
621    EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
622    System.out.println("Content type: "+eci.getContentType().getName());
623    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
624
625    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
626
627    // decrypt the content encryption key and the content
628    try {
629      System.out.println("Decrypt the content...");
630      if (recipientCert != null) {
631        enveloped_data.setupCipher(key, recipientCert);
632      } else {
633        // KEKRecipientInfo
634        enveloped_data.setupCipher(key, new KEKIdentifier(kekID));
635      }  
636      return enveloped_data.getContent();
637
638    } catch (InvalidKeyException ex) {
639      throw new CMSException("Private key error: "+ex.getMessage());
640    } catch (NoSuchAlgorithmException ex) {
641      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
642    }
643  }
644  
645  /**
646   * Creates the RecipientInfos.
647   *
648   * @return the RecipientInfos created, two KeyTransRecipientInfos, one
649   *         KeyAgreeRecipientInfo (for two recipients with same domain
650   *         parameters), and one KEKRecipientInfo
651   *
652   * @throws CMSException if an error occurs when creating the recipient infos
653   */
654  public RecipientInfo[] createRecipients() throws CMSException {
655    
656    RecipientInfo[] recipients = new RecipientInfo[4];
657    try {
658      // rsaUser1 is the first receiver (cert identified by IssuerAndSerialNumber)
659      recipients[0] = new KeyTransRecipientInfo(rsaUser1_, 
660                                                (AlgorithmID)AlgorithmID.rsaEncryption.clone());
661      // rsaUser2 is the second receiver (cert identifief by SubjectKeyIdentifier)
662      recipients[1] = new KeyTransRecipientInfo(rsaUser2_, 
663                                                CertificateIdentifier.SUBJECT_KEY_IDENTIFIER, 
664                                                (AlgorithmID)AlgorithmID.rsaEncryption.clone());
665
666      // next recipients use key agreement
667      // the key encryption (key agreement) algorithm to use:
668      AlgorithmID keyEA = (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone();
669      // the key wrap algorithm to use:
670      AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlg_.clone();
671      // the length of the key encryption key to be generated:
672      int kekLength = keyLength_;
673      recipients[2] = new KeyAgreeRecipientInfo(keyEA, keyWrapAlg, kekLength);
674      // esdhUser1 is the third receiver  (cert identified by IssuerAndSerialNumber)
675      ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(esdhUser1_, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
676      // esdhUser2 is the fourth receiver (cert identified by RecipientKeyIdentifier)
677      ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(esdhUser2_, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
678      
679      // last receiver uses a symmetric key encryption key  
680      AlgorithmID kea = (AlgorithmID)keyWrapAlg_.clone();
681      KEKIdentifier kekIdentifier = new KEKIdentifier(kekID_);
682      recipients[3] = new KEKRecipientInfo(kekIdentifier, kea, kek_);
683    } catch (Exception ex) {
684      throw new CMSException("Error adding recipients: " + ex.getMessage()); 
685    }    
686    return recipients;
687  }  
688  
689  /**
690   * Parses an EnvelopedData and decrypts the content for all test recipients
691   * using the index into the recipientInfos field for identifying the recipient.
692   *
693   * @param stream whether to use EnvelopedDataStream or EnvelopedData
694   * @param message the original message (to be compared to the decryption result) 
695   * @param encodedEnvelopedData the encoded EnvelopedData object 
696   *
697   * @throws Exception if some error occurs during decoding/decryption
698   */ 
699  public void parseEnvelopedDataWithRecipientInfoIndex(boolean stream, 
700    byte[] message, byte[] encodedEnvelopedData) throws Exception {
701    byte[] receivedMessage;
702    if (stream) {
703      // rsaUser1
704      System.out.println("\nDecrypt for rsaUser1:");
705      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1Pk_, 0);
706      if (!Arrays.equals(message, receivedMessage)) {
707        throw new IOException("Decryption error!");
708      }
709      System.out.print("\nDecrypted content: ");
710      System.out.println(new String(receivedMessage));
711      // rsaUser2
712      System.out.println("\nDecrypt for rsaUser2:");
713      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2Pk_, 1);
714      if (!Arrays.equals(message, receivedMessage)) {
715        throw new IOException("Decryption error!");
716      }
717      System.out.print("\nDecrypted content: ");
718      System.out.println(new String(receivedMessage));
719      // esdhUser1
720      System.out.println("\nDecrypt for esdhUser1:");
721      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1Pk_, 2);
722      if (!Arrays.equals(message, receivedMessage)) {
723        throw new IOException("Decryption error!");
724      }
725      System.out.print("\nDecrypted content: ");
726      System.out.println(new String(receivedMessage));
727      // esdhUser2
728      System.out.println("\nDecrypt for esdhUser2:");
729      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2Pk_, 2);
730      if (!Arrays.equals(message, receivedMessage)) {
731        throw new IOException("Decryption error!");
732      }
733      System.out.print("\nDecrypted content: ");
734      System.out.println(new String(receivedMessage));
735      // kekUser
736      System.out.println("\nDecrypt for kekUser:");
737      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek_, 3);
738      if (!Arrays.equals(message, receivedMessage)) {
739        throw new IOException("Decryption error!");
740      }
741      System.out.print("\nDecrypted content: ");
742      System.out.println(new String(receivedMessage));
743    } else {
744      // rsaUser1
745      System.out.println("\nDecrypt for rsaUser1:");
746      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1Pk_, 0);
747      if (!Arrays.equals(message, receivedMessage)) {
748        throw new IOException("Decryption error!");
749      }
750      System.out.print("\nDecrypted content: ");
751      System.out.println(new String(receivedMessage));
752       // rsaUser2
753      System.out.println("\nDecrypt for rsaUser2:");
754      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2Pk_, 1);
755      if (!Arrays.equals(message, receivedMessage)) {
756        throw new IOException("Decryption error!");
757      }
758      System.out.print("\nDecrypted content: ");
759      System.out.println(new String(receivedMessage));
760      // esdhUser1
761      System.out.println("\nDecrypt for esdhUser1:");
762      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1Pk_, 2);
763      if (!Arrays.equals(message, receivedMessage)) {
764        throw new IOException("Decryption error!");
765      }
766      System.out.print("\nDecrypted content: ");
767      System.out.println(new String(receivedMessage));
768      // esdhUser2
769      System.out.println("\nDecrypt for esdhUser2:");
770      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2Pk_, 2);
771      if (!Arrays.equals(message, receivedMessage)) {
772        throw new IOException("Decryption error!");
773      }
774      System.out.print("\nDecrypted content: ");
775      System.out.println(new String(receivedMessage));
776      // kekUser
777      System.out.println("\nDecrypt for kekUser:");
778      receivedMessage = getEnvelopedData(encodedEnvelopedData, kek_, 3);
779      if (!Arrays.equals(message, receivedMessage)) {
780        throw new IOException("Decryption error!");
781      }
782      System.out.print("\nDecrypted content: ");
783      System.out.println(new String(receivedMessage));
784    }    
785  }
786  
787  /**
788   * Parses an EnvelopedData and decrypts the content for all test recipients
789   * using their recipient identifiers for identifying the recipient.
790   *
791   * @param stream whether to use EnvelopedDataStream or EnvelopedData
792   * @param message the original message (to be compared to the decryption result) 
793   * @param encodedEnvelopedData the encoded EnvelopedData object 
794   *
795   * @throws Exception if some error occurs during decoding/decryption
796   */ 
797  public void parseEnvelopedDataWithRecipientIdentifier(boolean stream, 
798    byte[] message, byte[] encodedEnvelopedData) throws Exception {
799    byte[] receivedMessage;
800    if (stream) {
801      // rsaUser1
802      System.out.println("\nDecrypt for rsaUser1:");
803      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1Pk_, new IssuerAndSerialNumber(rsaUser1_));
804      if (!Arrays.equals(message, receivedMessage)) {
805        throw new IOException("Decryption error!");
806      }
807      System.out.print("\nDecrypted content: ");
808      System.out.println(new String(receivedMessage));
809      // rsaUser2
810      System.out.println("\nDecrypt for rsaUser2:");
811      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2Pk_, new SubjectKeyID(rsaUser2_));
812      if (!Arrays.equals(message, receivedMessage)) {
813        throw new IOException("Decryption error!");
814      }
815      System.out.print("\nDecrypted content: ");
816      System.out.println(new String(receivedMessage));
817      // esdhUser1
818      System.out.println("\nDecrypt for esdhUser1:");
819      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1Pk_, new IssuerAndSerialNumber(esdhUser1_));
820      if (!Arrays.equals(message, receivedMessage)) {
821        throw new IOException("Decryption error!");
822      }
823      System.out.print("\nDecrypted content: ");
824      System.out.println(new String(receivedMessage));
825      // esdhUser2
826      System.out.println("\nDecrypt for esdhUser2:");
827      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2Pk_, new RecipientKeyIdentifier(esdhUser2_));
828      if (!Arrays.equals(message, receivedMessage)) {
829        throw new IOException("Decryption error!");
830      }
831      System.out.print("\nDecrypted content: ");
832      System.out.println(new String(receivedMessage));
833      // kekUser
834      System.out.println("\nDecrypt for kekUser:");
835      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek_, new KEKIdentifier(kekID_));
836      if (!Arrays.equals(message, receivedMessage)) {
837        throw new IOException("Decryption error!");
838      }
839      System.out.print("\nDecrypted content: ");
840      System.out.println(new String(receivedMessage));
841    } else {
842      // rsaUser1
843      System.out.println("\nDecrypt for rsaUser1:");
844      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1Pk_, new IssuerAndSerialNumber(rsaUser1_));
845      if (!Arrays.equals(message, receivedMessage)) {
846        throw new IOException("Decryption error!");
847      }
848      System.out.print("\nDecrypted content: ");
849      System.out.println(new String(receivedMessage));
850       // rsaUser2
851      System.out.println("\nDecrypt for rsaUser2:");
852      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2Pk_, new SubjectKeyID(rsaUser2_));
853      if (!Arrays.equals(message, receivedMessage)) {
854        throw new IOException("Decryption error!");
855      }
856      System.out.print("\nDecrypted content: ");
857      System.out.println(new String(receivedMessage));
858      // esdhUser1
859      System.out.println("\nDecrypt for esdhUser1:");
860      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1Pk_, new IssuerAndSerialNumber(esdhUser1_));
861      if (!Arrays.equals(message, receivedMessage)) {
862        throw new IOException("Decryption error!");
863      }
864      System.out.print("\nDecrypted content: ");
865      System.out.println(new String(receivedMessage));
866      // esdhUser2
867      System.out.println("\nDecrypt for esdhUser2:");
868      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2Pk_, new RecipientKeyIdentifier(esdhUser2_));
869      if (!Arrays.equals(message, receivedMessage)) {
870        throw new IOException("Decryption error!");
871      }
872      System.out.print("\nDecrypted content: ");
873      System.out.println(new String(receivedMessage));
874      // kekUser
875      System.out.println("\nDecrypt for kekUser:");
876      receivedMessage = getEnvelopedData(encodedEnvelopedData, kek_, new KEKIdentifier(kekID_));
877      if (!Arrays.equals(message, receivedMessage)) {
878        throw new IOException("Decryption error!");
879      }
880      System.out.print("\nDecrypted content: ");
881      System.out.println(new String(receivedMessage));
882    }    
883  }
884  
885  /**
886   * Parses an EnvelopedData and decrypts the content for all test recipients
887   * using their recipient certificate (for RecipientInfos of type KeyTransRecipientInfo
888   * or KeyAgreeRecipientInfo) or key id (for RecipientInfos of type KEKRecipientInfo)
889   * for identifying the recipient.
890   *
891   * @param stream whether to use EnvelopedDataStream or EnvelopedData
892   * @param message the original message (to be compared to the decryption result) 
893   * @param encodedEnvelopedData the encoded EnvelopedData object 
894   *
895   * @throws Exception if some error occurs during decoding/decryption
896   */ 
897  public void parseEnvelopedDataWithRecipientCertOrKEKId(boolean stream, 
898    byte[] message, byte[] encodedEnvelopedData) throws Exception {
899    byte[] receivedMessage;
900    if (stream) {
901      // rsaUser1
902      System.out.println("\nDecrypt for rsaUser1:");
903      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1Pk_, rsaUser1_, null);
904      if (!Arrays.equals(message, receivedMessage)) {
905        throw new IOException("Decryption error!");
906      }
907      System.out.print("\nDecrypted content: ");
908      System.out.println(new String(receivedMessage));
909      // rsaUser2
910      System.out.println("\nDecrypt for rsaUser2:");
911      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2Pk_, rsaUser2_, null);
912      if (!Arrays.equals(message, receivedMessage)) {
913        throw new IOException("Decryption error!");
914      }
915      System.out.print("\nDecrypted content: ");
916      System.out.println(new String(receivedMessage));
917      // esdhUser1
918      System.out.println("\nDecrypt for esdhUser1:");
919      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1Pk_, esdhUser1_, null);
920      if (!Arrays.equals(message, receivedMessage)) {
921        throw new IOException("Decryption error!");
922      }
923      System.out.print("\nDecrypted content: ");
924      System.out.println(new String(receivedMessage));
925      // esdhUser2
926      System.out.println("\nDecrypt for esdhUser2:");
927      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2Pk_, esdhUser2_, null);
928      if (!Arrays.equals(message, receivedMessage)) {
929        throw new IOException("Decryption error!");
930      }
931      System.out.print("\nDecrypted content: ");
932      System.out.println(new String(receivedMessage));
933      // kekUser
934      System.out.println("\nDecrypt for kekUser:");
935      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek_, null, kekID_);
936      if (!Arrays.equals(message, receivedMessage)) {
937        throw new IOException("Decryption error!");
938      }
939      System.out.print("\nDecrypted content: ");
940      System.out.println(new String(receivedMessage));
941    } else {
942      // rsaUser1
943      System.out.println("\nDecrypt for rsaUser1:");
944      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1Pk_, rsaUser1_, null);
945      if (!Arrays.equals(message, receivedMessage)) {
946        throw new IOException("Decryption error!");
947      }
948      System.out.print("\nDecrypted content: ");
949      System.out.println(new String(receivedMessage));
950       // rsaUser2
951      System.out.println("\nDecrypt for rsaUser2:");
952      receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2Pk_, rsaUser2_, null);
953      if (!Arrays.equals(message, receivedMessage)) {
954        throw new IOException("Decryption error!");
955      }
956      System.out.print("\nDecrypted content: ");
957      System.out.println(new String(receivedMessage));
958      // esdhUser1
959      System.out.println("\nDecrypt for esdhUser1:");
960      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1Pk_, esdhUser1_, null);
961      if (!Arrays.equals(message, receivedMessage)) {
962        throw new IOException("Decryption error!");
963      }
964      System.out.print("\nDecrypted content: ");
965      System.out.println(new String(receivedMessage));
966      // esdhUser2
967      System.out.println("\nDecrypt for esdhUser2:");
968      receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2Pk_, esdhUser2_, null);
969      if (!Arrays.equals(message, receivedMessage)) {
970        throw new IOException("Decryption error!");
971      }
972      System.out.print("\nDecrypted content: ");
973      System.out.println(new String(receivedMessage));
974      // kekUser
975      System.out.println("\nDecrypt for kekUser:");
976      receivedMessage = getEnvelopedData(encodedEnvelopedData, kek_, null, kekID_);
977      if (!Arrays.equals(message, receivedMessage)) {
978        throw new IOException("Decryption error!");
979      }
980      System.out.print("\nDecrypted content: ");
981      System.out.println(new String(receivedMessage));
982    }    
983  }
984  
985  /**
986   * Starts the test.
987   */
988  public void start() {
989     // the test message
990    String m = "This is the test message.";
991    System.out.println("Test message: \""+m+"\"");
992    System.out.println();
993    byte[] message = m.getBytes();
994
995    try {
996      byte[] data;
997      System.out.println("Stream implementation demos");
998      System.out.println("===========================");
999
1000
1001      // the stream implementation
1002      //
1003      // test CMS EnvelopedDataStream
1004      //
1005      System.out.println("\nCMS EnvelopedDataStream demo [create]:\n");
1006      data = createEnvelopedDataStream(message);
1007      // transmit data
1008      System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n");
1009      System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1010      parseEnvelopedDataWithRecipientInfoIndex(true, message, data);
1011      System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1012      parseEnvelopedDataWithRecipientIdentifier(true, message, data);
1013      System.out.println("Decrypt for the several recipients using their certificate or symmetric kek.");
1014      parseEnvelopedDataWithRecipientCertOrKEKId(true, message, data);
1015
1016      // the non-stream implementation
1017      System.out.println("\nNon-stream implementation demos");
1018      System.out.println("===============================");
1019
1020            
1021      //
1022      // test CMS EnvelopedData
1023      //
1024      System.out.println("\nCMS EnvelopedData demo [create]:\n");
1025      data = createEnvelopedData(message);
1026      // transmit data
1027      System.out.println("\nCMS EnvelopedData demo [parse]:\n");
1028      System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1029      parseEnvelopedDataWithRecipientInfoIndex(false, message, data);
1030      System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1031      parseEnvelopedDataWithRecipientIdentifier(false, message, data);
1032      System.out.println("Decrypt for the several recipients using their certificate or symmetric kek.");
1033      parseEnvelopedDataWithRecipientCertOrKEKId(false, message, data);
1034      
1035
1036        } catch (Exception ex) {
1037          ex.printStackTrace();
1038          throw new RuntimeException(ex.toString());
1039        }
1040  }
1041  
1042  /**
1043   * Main method.
1044   *
1045   * @throws IOException
1046   *            if an I/O error occurs when reading required keys
1047   *            and certificates from files
1048   */
1049  public static void main(String argv[]) throws Exception {
1050
1051        DemoUtil.initDemos();
1052    (new EnvelopedDataDemo()).start();
1053    System.out.println("\nReady!");
1054    DemoUtil.waitKey();
1055  }
1056}