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