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