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/smime/basic/CMSStreamDemo.java 31    12.02.25 17:58 Dbratko $
059    // $Revision: 31 $
060    //
061    
062    package demo.smime.basic;
063    
064    import java.io.ByteArrayInputStream;
065    import java.io.ByteArrayOutputStream;
066    import java.io.IOException;
067    import java.io.InputStream;
068    import java.io.OutputStream;
069    import java.io.PipedInputStream;
070    import java.io.PipedOutputStream;
071    import java.security.PrivateKey;
072    import java.util.Random;
073    
074    import demo.DemoSMimeUtil;
075    import demo.DemoUtil;
076    import demo.keystore.CMSKeyStore;
077    import iaik.asn1.structures.AlgorithmID;
078    import iaik.cms.IssuerAndSerialNumber;
079    import iaik.smime.SMimeAuthEncrypted;
080    import iaik.smime.SMimeEncrypted;
081    import iaik.smime.SMimeSigned;
082    import iaik.utils.CryptoUtils;
083    import iaik.utils.Util;
084    import iaik.x509.X509Certificate;
085    
086    /**
087     * This class shows the usage of the SMimeSigned and SMimeEncrypted classes. 
088     * These classes can be used to create/parse signed and/or encrypted CMS messages.
089     * This demo does not use the JavaMail API.
090     *
091     * @see iaik.smime.SMimeSigned
092     * @see iaik.smime.SMimeEncrypted
093     */
094    public class CMSStreamDemo {
095    
096      // the signer private key
097      PrivateKey[] signerPrivateKeys;
098      // the signer certificate
099      X509Certificate rsaSignerCertificate;
100      // the signer certificate
101      X509Certificate dsaSignerCertificate;
102      // the recipient private key
103      PrivateKey[] recipientPrivateKeys;
104      // the recipient certificate
105      X509Certificate[] recipientCertificates;
106      // the certificate chain
107      X509Certificate[] signerCertificates;
108      // buffer with test data to sign and/or encrypt
109      byte[] buffer;
110      // size of the buffer
111      final static int BUF_SIZE = 10000;
112      
113      /**
114       * Empty default constructor.
115       */
116      public CMSStreamDemo() {
117        
118        System.out.println();
119        System.out.println("******************************************************************************************");
120        System.out.println("*                                   CMSStreamDemo                                        *");
121        System.out.println("*       (shows the usage of the IAIK-CMS SMimeSigned and SMimeEncrypted classes)         *");
122        System.out.println("******************************************************************************************");
123        System.out.println();
124        
125        // create some random data for the test
126        buffer = new byte[BUF_SIZE];
127        Random rnd = new Random();
128        rnd.nextBytes(buffer);
129      }
130      
131      /**
132       * Reads the required keys and certificates from the demo keystore.
133       * 
134       * @throws Exception if some error occurs when reading from the keystore
135       */
136      public void setupCertificates() throws Exception {
137        // get the certificates from the KeyStore
138        // we use two signers just for demonstration
139        signerPrivateKeys = new PrivateKey[2];
140        // RSA signer
141        X509Certificate[] rsaCerts = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
142        rsaSignerCertificate = rsaCerts[0];
143        signerPrivateKeys[0] = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
144        // DSA signer
145        X509Certificate[] dsaCerts = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
146        dsaSignerCertificate = dsaCerts[0];
147        signerPrivateKeys[1] = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
148        signerCertificates = new X509Certificate[rsaCerts.length + dsaCerts.length];
149        System.arraycopy(rsaCerts, 0, signerCertificates, 0, dsaCerts.length);
150        System.arraycopy(dsaCerts, 0, signerCertificates, rsaCerts.length, dsaCerts.length);
151    
152        // get the recipients keys and certificates
153        recipientPrivateKeys = new PrivateKey[2];
154        recipientPrivateKeys[0] = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
155        recipientPrivateKeys[1] = CMSKeyStore.getPrivateKey(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1);
156        recipientCertificates = new X509Certificate[2];
157        recipientCertificates[0] = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
158        recipientCertificates[1] = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1)[0];
159      }
160    
161      /**
162       * Uses class {@link iaik.smime.SMimeEncrypted SMimeEncrypted} to encrypt some data, encode it,
163       * and finally parses the encoding to decrypt and recover the original content.
164       *
165       * @param contentEA the content encryption algorithm to be used
166       * @param keyWrapAlg the key wrap algorithm to be used for encrypting the temporary content encryption key
167       * @param keyLength the length of the content encryption key to be created and used
168       * @param recipientIndex the index into the recipientInfos field indicating for which recipient the message
169       *                       shall be decrypted
170       *
171       * @throws Exception if some error occurs
172       */
173      public void testSMimeEncrypted(AlgorithmID contentEA, AlgorithmID keyWrapAlg, int keyLength, int recipientIndex) throws Exception {
174    
175        // repository for the encrypted message
176        byte[] encrypted_message;
177        // the InputStream containing the data to encrypt
178        InputStream is = new ByteArrayInputStream(buffer);
179        // the OutputStream where the data shall be written to
180        ByteArrayOutputStream os = new ByteArrayOutputStream();
181    
182        // encrypt with DES/CBC and default key length
183        SMimeEncrypted encrypted = new SMimeEncrypted(is, contentEA, keyLength);
184        // add one RSA recipient
185        encrypted.addRecipient(recipientCertificates[0], (AlgorithmID)AlgorithmID.rsaEncryption.clone());
186        // add one ESDH recipient
187        encrypted.addRecipient(recipientCertificates[1], (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), keyWrapAlg, keyLength);
188        
189        // encrypt and write the data to the output stream
190        encrypted.writeTo(os);
191        // finished
192        os.close();
193        // get the encrypted message from the ByteArrayOutputStream
194        encrypted_message = os.toByteArray();
195        
196        // and now decrypt the data
197        is = new ByteArrayInputStream(encrypted_message);
198        encrypted = new SMimeEncrypted(is);
199        // use this private key to decrypt the symmetric key of recipient 0
200        encrypted.decryptSymmetricKey(recipientPrivateKeys[recipientIndex], recipientIndex);
201        // get the InputStream with the decrypted data
202        InputStream data = encrypted.getInputStream();
203    
204        // reset our output stream
205        os.reset();
206        // write the decrypted data to an output stream
207        // copy the data
208        Util.copyStream(data, os, null);
209        os.close();
210    
211        // original and 'received' message must be the same
212        if (!CryptoUtils.equalsBlock(buffer, os.toByteArray()))
213          throw new RuntimeException("Error: messages are not equal!");
214      }
215    
216      /**
217       * Uses class {@link iaik.smime.SMimeSigned SMimeSigned} to sign some data, encode it,
218       * and finally parses the encoding to verify the signature.
219       *
220       * @param mode either {@link iaik.smime.SMimeSigned#IMPLICIT implicit} or
221       *                    {@link iaik.smime.SMimeSigned#EXPLICIT explicit} to indicate
222       *                    whether the content shall be included in the signature or
223       *                    transmitted out-of-band
224       * 
225       * @throws Exception if some error occurs
226       */
227      public void testSMimeSigned(int mode) throws Exception {
228    
229        // repository for the signed message
230        byte[] signed_message = null;
231        // repository for the signature
232        byte[] signature;
233        // the InputStream containing the data to sign
234        InputStream is = new ByteArrayInputStream(buffer);
235        // the OutputStream where the data shall be written to
236        ByteArrayOutputStream os = new ByteArrayOutputStream();
237    
238        // create an implicitly/explicitly signed message
239        SMimeSigned signed = new SMimeSigned(is, mode);
240        // these certificates are sent within the signature
241        signed.setCertificates(signerCertificates);
242        // add two signers for testing
243        signed.addSigner(signerPrivateKeys[0], 
244                         new IssuerAndSerialNumber(rsaSignerCertificate));
245        signed.addSigner(signerPrivateKeys[1], 
246                         new IssuerAndSerialNumber(dsaSignerCertificate), 
247                         (AlgorithmID)AlgorithmID.sha256.clone(),
248                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone());
249        
250    
251        if (mode == SMimeSigned.EXPLICIT) {
252          // write the data to the out-of-band file
253          ByteArrayOutputStream out_data = new ByteArrayOutputStream();
254          Util.copyStream(signed.getInputStream(), out_data, null);
255          out_data.close();
256          signed_message = out_data.toByteArray();
257        }
258    
259        // write the signature or the data+signature to the output stream
260        signed.writeTo(os);
261        os.close();
262        signature = os.toByteArray();
263    
264        // and now verify the signature
265        is = new ByteArrayInputStream(signature);
266        signed = new SMimeSigned(is);
267        if (mode == SMimeSigned.EXPLICIT) {
268          // content data has been not included 
269          signed.setInputStream(new ByteArrayInputStream(signed_message));
270        }
271        
272        // get the InputStream with the signed, plain data
273        InputStream data = signed.getInputStream();
274    
275        // reset our output stream
276        os.reset();
277        // write the verified data to the output stream
278        // copy the data
279        Util.copyStream(data, os, null);
280        os.close();
281    
282        // now verify the signed data and print the certificate of the signer
283        // (verify() verifies signer at index 0)
284        X509Certificate cert = signed.verify();
285        System.out.println("Signature OK from: "+cert.getSubjectDN());
286        // verify our second test signer
287        cert = signed.verify(1);
288        System.out.println("Signature OK from: "+cert.getSubjectDN());
289    
290        // original and 'received' message must be the same
291        if (!CryptoUtils.equalsBlock(buffer, os.toByteArray()))
292          throw new RuntimeException("Error: messages are not equal!");
293      }
294      
295      /**
296       * Uses class {@link iaik.smime.SMimeAuthEncrypted SMimeAuthEncrypted} to 
297       * authenticated encrypt some data, encode it,
298       * and finally parses the encoding to decrypt and recover the original content.
299       *
300       * @param contentEA the content-authenticated encryption algorithm to be used
301       * @param keyWrapAlg the key wrap algorithm to be used for encrypting the temporary content encryption key
302       * @param keyLength the length of the content encryption key to be created and used
303       * @param recipientIndex the index into the recipientInfos field indicating for which recipient the message
304       *                       shall be decrypted
305       *
306       * @throws Exception if some error occurs
307       */
308      public void testSMimeAuthEncrypted(AlgorithmID contentEA, AlgorithmID keyWrapAlg, int keyLength, int recipientIndex) throws Exception {
309    
310        // repository for the encrypted message
311        byte[] encrypted_message;
312        // the InputStream containing the data to encrypt
313        InputStream is = new ByteArrayInputStream(buffer);
314        // the OutputStream where the data shall be written to
315        ByteArrayOutputStream os = new ByteArrayOutputStream();
316    
317        // encrypt with DES/CBC and default key length
318        SMimeAuthEncrypted encrypted = new SMimeAuthEncrypted(is, contentEA, keyLength);
319        if (contentEA.equals(AlgorithmID.aes128_CCM) || 
320            contentEA.equals(AlgorithmID.aes192_CCM) ||
321            contentEA.equals(AlgorithmID.aes256_CCM)) {
322          // for aes-ccm we need to know the data input length in advance
323          encrypted.setInputLength(buffer.length);
324        }
325        
326        // add one RSA recipient
327        encrypted.addRecipient(recipientCertificates[0], (AlgorithmID)AlgorithmID.rsaEncryption.clone());
328        // add one ESDH recipient
329        encrypted.addRecipient(recipientCertificates[1], (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(), keyWrapAlg, keyLength);
330        
331        // encrypt and write the data to the output stream
332        encrypted.writeTo(os);
333        // finished
334        os.close();
335        // get the encrypted message from the ByteArrayOutputStream
336        encrypted_message = os.toByteArray();
337        
338        // and now decrypt the data
339        is = new ByteArrayInputStream(encrypted_message);
340        encrypted = new SMimeAuthEncrypted(is, (byte[])null, buffer.length);
341        // use this private key to decrypt the symmetric key of recipient 0
342        encrypted.decryptSymmetricKey(recipientPrivateKeys[recipientIndex], recipientIndex);
343        // get the InputStream with the decrypted data
344        InputStream data = encrypted.getInputStream();
345    
346        // reset our output stream
347        os.reset();
348        // write the decrypted data to an output stream
349        // copy the data
350        Util.copyStream(data, os, null);
351        os.close();
352    
353        // original and 'received' message must be the same
354        if (!CryptoUtils.equalsBlock(buffer, os.toByteArray()))
355          throw new RuntimeException("Error: messages are not equal!");
356      }
357      
358      /**
359       * Uses class {@link iaik.smime.SMimeSigned SMimeSigned} and class {@link iaik.smime.SMimeEncrypted SMimeEncrypted}
360       * to sign and encrypt some data, encode it, and finally parses the encoding to decrypt and recover the original content
361       * and verify the signature.
362       *
363       * @param contentEA the content encryption algorithm to be used
364       * @param keyWrapAlg the key wrap algorithm to be used for encrypting the temporary content encryption key
365       * @param keyLength the length of the content encryption key to be created and used
366       * @param recipientIndex the index into the recipientInfos field indicating for which recipient the message
367       *                       shall be decrypted
368       *
369       * @throws Exception if some error occurs
370       */
371      public void testSMimeSignedAndEncrypted(AlgorithmID contentEA, AlgorithmID keyWrapAlg, int keyLength, int recipientIndex) throws Exception {
372    
373        // repository for the signed and encrypted message
374        byte[] signed_encrpyted_message;
375        // the InputStream containing the data to sign and encrypt
376        InputStream is = new ByteArrayInputStream(buffer);
377        // the OutputStream where the data shall be written to
378        ByteArrayOutputStream os = new ByteArrayOutputStream();
379    
380        // we have to sign and encrypt => connect 2 streams
381        PipedOutputStream piped_out = new PipedOutputStream();
382        PipedInputStream piped_in = new PipedInputStream(piped_out);
383    
384        // create an implicit signed message (signature contains message)
385        SMimeSigned signed = new SMimeSigned(is, SMimeSigned.IMPLICIT);
386        // these certificates are sent within the signature
387        signed.setCertificates(signerCertificates);
388        // add two signers for testing
389        signed.addSigner(signerPrivateKeys[0],
390                         new IssuerAndSerialNumber(rsaSignerCertificate));
391        signed.addSigner(signerPrivateKeys[1], new IssuerAndSerialNumber(dsaSignerCertificate),
392                         (AlgorithmID)AlgorithmID.sha256.clone(),
393                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone());
394    
395    
396        // a new Thread between the 2 streams
397        Writer writer = new Writer(signed, piped_out);
398        writer.start();
399    
400        // encrypt with DES/CBC and default key length
401        SMimeEncrypted encrypted = new SMimeEncrypted(piped_in, contentEA, keyLength);
402    
403        // add one RSA recipient
404        encrypted.addRecipient(recipientCertificates[0],
405                               (AlgorithmID)AlgorithmID.rsaEncryption.clone());
406        // add one ESDH recipient
407        encrypted.addRecipient(recipientCertificates[1], 
408                               (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(),
409                               keyWrapAlg, 
410                               keyLength);
411        // encrypt and write the data to the output stream
412        encrypted.writeTo(os);
413        // finished
414        os.close();
415        // get the signed and encrypted message from the ByteArrayOutputStream
416        signed_encrpyted_message = os.toByteArray();
417    
418        // and now decrypt the data and verify the signature
419        is = new ByteArrayInputStream(signed_encrpyted_message);
420        encrypted = new SMimeEncrypted(is);
421        // use this private key to decrypt the symmetric key
422        encrypted.decryptSymmetricKey(recipientPrivateKeys[recipientIndex], recipientIndex);
423        // get the InputStream with the decrypted data
424        InputStream data_dec = encrypted.getInputStream();
425    
426        // read the signed data from the derypted InputStream
427        signed = new SMimeSigned(data_dec);
428    
429        // get the InputStream with the signed, plain data
430        InputStream data = signed.getInputStream();
431    
432        // reset our output stream
433        os.reset();
434        // write the decrypted and verified data to the output stream
435        // copy the data
436        Util.copyStream(data, os, null);
437        os.close();
438    
439        // now verify the signed data and print the certificate of the signer
440        // (verify() verifies signer at index 0)
441        X509Certificate cert = signed.verify();
442        System.out.println("Signature OK from: "+cert.getSubjectDN());
443        // verify our second test signer
444        cert = signed.verify(1);
445        System.out.println("Signature OK from: "+cert.getSubjectDN());
446    
447        // original and 'received' message must be the same
448        if (!CryptoUtils.equalsBlock(buffer, os.toByteArray()))
449          throw new RuntimeException("Error: messages are not equal!");
450      }
451      
452      /**
453       * Uses class {@link iaik.smime.SMimeSigned SMimeSigned} and class {@link iaik.smime.SMimeAuthEncrypted SMimeAuthEncrypted}
454       * to sign and authenticated encrypt some data, encode it, and finally parses the encoding to decrypt and recover the original content
455       * and verify the signature.
456       *
457       * @param contentEA the content-authenticated encryption algorithm to be used
458       * @param keyWrapAlg the key wrap algorithm to be used for encrypting the temporary content encryption key
459       * @param keyLength the length of the content encryption key to be created and used
460       * @param recipientIndex the index into the recipientInfos field indicating for which recipient the message
461       *                       shall be decrypted
462       *
463       * @throws Exception if some error occurs
464       */
465      public void testSMimeSignedAndAuthEncrypted(AlgorithmID contentEA, AlgorithmID keyWrapAlg, int keyLength, int recipientIndex) throws Exception {
466    
467        // repository for the signed and encrypted message
468        byte[] signed_encrpyted_message;
469        // the InputStream containing the data to sign and encrypt
470        InputStream is = new ByteArrayInputStream(buffer);
471        // the OutputStream where the data shall be written to
472        ByteArrayOutputStream os = new ByteArrayOutputStream();
473    
474        // we have to sign and encrypt => connect 2 streams
475        PipedOutputStream piped_out = new PipedOutputStream();
476        PipedInputStream piped_in = new PipedInputStream(piped_out);
477    
478        // create an implicit signed message (signature contains message)
479        SMimeSigned signed = new SMimeSigned(is, SMimeSigned.IMPLICIT);
480        // these certificates are sent within the signature
481        signed.setCertificates(signerCertificates);
482        // add two signers for testing
483        signed.addSigner(signerPrivateKeys[0],
484                         new IssuerAndSerialNumber(rsaSignerCertificate));
485        signed.addSigner(signerPrivateKeys[1], new IssuerAndSerialNumber(dsaSignerCertificate),
486                         (AlgorithmID)AlgorithmID.sha256.clone(),
487                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone());
488    
489    
490        // a new Thread between the 2 streams
491        Writer writer = new Writer(signed, piped_out);
492        writer.start();
493    
494        // encrypt with default key length
495        SMimeAuthEncrypted encrypted = new SMimeAuthEncrypted(piped_in, contentEA, keyLength);
496        
497        // add one RSA recipient
498        encrypted.addRecipient(recipientCertificates[0],
499                               (AlgorithmID)AlgorithmID.rsaEncryption.clone());
500        // add one ESDH recipient
501        encrypted.addRecipient(recipientCertificates[1], 
502                               (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(),
503                               keyWrapAlg, 
504                               keyLength);
505        // encrypt and write the data to the output stream
506        encrypted.writeTo(os);
507        // finished
508        os.close();
509        // get the signed and encrypted message from the ByteArrayOutputStream
510        signed_encrpyted_message = os.toByteArray();
511    
512        // and now decrypt the data and verify the signature
513        is = new ByteArrayInputStream(signed_encrpyted_message);
514        encrypted = new SMimeAuthEncrypted(is, (byte[])null, -1);
515        
516        // use this private key to decrypt the symmetric key
517        encrypted.decryptSymmetricKey(recipientPrivateKeys[recipientIndex], recipientIndex);
518        // get the InputStream with the decrypted data
519        InputStream decryptedIs = encrypted.getInputStream();
520    
521        // read the signed data from the derypted InputStream
522        signed = new SMimeSigned(decryptedIs);
523    
524        // get the InputStream with the signed, plain data
525        InputStream data = signed.getInputStream();
526    
527        // reset our output stream
528        os.reset();
529        // write the decrypted and verified data to the output stream
530        // copy the data
531        Util.copyStream(data, os, null);
532        os.close();
533    
534        // now verify the signed data and print the certificate of the signer
535        // (verify() verifies signer at index 0)
536        X509Certificate cert = signed.verify();
537        System.out.println("Signature OK from: "+cert.getSubjectDN());
538        // verify our second test signer
539        cert = signed.verify(1);
540        System.out.println("Signature OK from: "+cert.getSubjectDN());
541    
542        // original and 'received' message must be the same
543        if (!CryptoUtils.equalsBlock(buffer, os.toByteArray()))
544          throw new RuntimeException("Error: messages are not equal!");
545      }
546      
547      /**
548       * Starts the demo.
549       */
550      public void start() {
551        try {
552          // read keys and certificates from demo keystore  
553          setupCertificates();
554    
555          System.out.println("testing an implicit S/MIME signed message...");
556          testSMimeSigned(SMimeSigned.IMPLICIT);
557    
558          System.out.println("testing an explicit S/MIME signed message...");
559          testSMimeSigned(SMimeSigned.EXPLICIT);
560          
561          // DES EDE is deprecated; only demonstrated here
562          AlgorithmID contentEA =  (AlgorithmID)AlgorithmID.des_EDE3_CBC.clone();
563          AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_3DES_wrap.clone();
564          System.out.println("testing a S/MIME encrypted message for 3DES CBC; decrypting for RSA user...");
565          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 0);
566          System.out.println("testing a S/MIME encrypted message for 3DES CBC; decrypting for ESDH user...");
567          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 1);
568          
569          // RC2 is deprecated; only demonstrated here
570          contentEA = (AlgorithmID)AlgorithmID.rc2_CBC.clone();
571          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_rc2_wrap.clone();
572          System.out.println("testing a S/MIME encrypted message for RC2 CBC; decrypting for RSA user...");
573          testSMimeEncrypted(contentEA, keyWrapAlg, 128, 0);
574          System.out.println("testing a S/MIME encrypted message for RC2 CBC; decrypting for ESDH user...");
575          testSMimeEncrypted(contentEA, keyWrapAlg, 128, 1);
576          
577          // DES EDE is deprecated; only demonstrated here
578          contentEA = (AlgorithmID)AlgorithmID.des_EDE3_CBC.clone();
579          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_3DES_wrap.clone();
580          System.out.println("testing a S/MIME signed and encrypted message for 3DES CBC; decrypting for RSA user...");
581              testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 0);
582              System.out.println("testing a S/MIME signed and encrypted message for 3DES CBC; decrypting for ESDH user...");
583              testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 1);
584              
585          // RC2 is deprecated; only demonstrated here
586              contentEA = (AlgorithmID)AlgorithmID.rc2_CBC.clone();
587              keyWrapAlg = (AlgorithmID)AlgorithmID.cms_rc2_wrap.clone();
588          System.out.println("testing a S/MIME signed and encrypted message for RC2 CBC; decrypting for RSA user...");
589              testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, 128, 0);
590              System.out.println("testing a S/MIME signed and encrypted message for RC2 CBC; decrypting for ESDH user...");
591              testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, 128, 1);
592              
593              contentEA =  (AlgorithmID)AlgorithmID.aes128_CBC.clone();
594          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
595          System.out.println("testing a S/MIME encrypted message for AES-128-CBC; decrypting for RSA user...");
596          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 0);
597          System.out.println("testing a S/MIME encrypted message for AES-128-CBC; decrypting for ESDH user...");
598          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 1);
599          
600          contentEA =  (AlgorithmID)AlgorithmID.aes128_CBC.clone();
601          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
602          System.out.println("testing a S/MIME signed and encrypted message for AES-128-CBC; decrypting for RSA user...");
603          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 0);
604          System.out.println("testing a S/MIME signed and encrypted message for AES-128-CBC; decrypting for ESDH user...");
605          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 1);
606          
607          contentEA =  (AlgorithmID)AlgorithmID.aes192_CBC.clone();
608          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
609          System.out.println("testing a S/MIME encrypted message for AES-192-CBC; decrypting for RSA user...");
610          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 0);
611          System.out.println("testing a S/MIME encrypted message for AES-192-CBC; decrypting for ESDH user...");
612          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 1);
613          
614          contentEA =  (AlgorithmID)AlgorithmID.aes192_CBC.clone();
615          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
616          System.out.println("testing a S/MIME signed and encrypted message for AES-192-CBC; decrypting for RSA user...");
617          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 0);
618          System.out.println("testing a S/MIME signed and encrypted message for AES-192-CBC; decrypting for ESDH user...");
619          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 1);      
620          
621          contentEA =  (AlgorithmID)AlgorithmID.aes256_CBC.clone();
622          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
623          System.out.println("testing a S/MIME encrypted message for AES-256-CBC; decrypting for RSA user...");
624          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 0);
625          System.out.println("testing a S/MIME encrypted message for AES-256-CBC; decrypting for ESDH user...");
626          testSMimeEncrypted(contentEA, keyWrapAlg, -1, 1);
627          contentEA =  (AlgorithmID)AlgorithmID.aes256_CBC.clone();
628          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
629          System.out.println("testing a S/MIME signed and encrypted message for AES-256-CBC; decrypting for RSA user...");
630          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 0);
631          System.out.println("testing a S/MIME signed and encrypted message for AES-256-CBC; decrypting for ESDH user...");
632          testSMimeSignedAndEncrypted(contentEA, keyWrapAlg, -1, 1);        
633               
634          contentEA =  (AlgorithmID)AlgorithmID.aes128_GCM.clone();
635          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
636          System.out.println("testing a S/MIME auth encrypted message for AES-128-GCM; decrypting for RSA user...");
637          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
638          System.out.println("testing a S/MIME auth encrypted message for AES-128-GCM; decrypting for ESDH user...");
639          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
640          
641          contentEA =  (AlgorithmID)AlgorithmID.aes192_GCM.clone();
642          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
643          System.out.println("testing a S/MIME auth encrypted message for AES-192-GCM; decrypting for RSA user...");
644          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
645          System.out.println("testing a S/MIME auth encrypted message for AES-192-GCM; decrypting for ESDH user...");
646          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
647          
648          contentEA =  (AlgorithmID)AlgorithmID.aes256_GCM.clone();
649          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
650          System.out.println("testing a S/MIME auth encrypted message for AES-256-GCM; decrypting for RSA user...");
651          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
652          System.out.println("testing a S/MIME auth encrypted message for AES-256-GCM; decrypting for ESDH user...");
653          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
654          
655          contentEA =  (AlgorithmID)AlgorithmID.aes128_CCM.clone();
656          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
657          System.out.println("testing a S/MIME auth encrypted message for AES-128-CCM; decrypting for RSA user...");
658          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
659          System.out.println("testing a S/MIME auth encrypted message for AES-128-CCM; decrypting for ESDH user...");
660          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
661          
662          contentEA =  (AlgorithmID)AlgorithmID.aes192_CCM.clone();
663          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
664          System.out.println("testing a S/MIME auth encrypted message for AES-192-CCM; decrypting for RSA user...");
665          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
666          System.out.println("testing a S/MIME auth encrypted message for AES-192-CCM; decrypting for ESDH user...");
667          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
668          
669          contentEA =  (AlgorithmID)AlgorithmID.aes256_CCM.clone();
670          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
671          System.out.println("testing a S/MIME auth encrypted message for AES-256-CCM; decrypting for RSA user...");
672          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
673          System.out.println("testing a S/MIME auth encrypted message for AES-256-CCM; decrypting for ESDH user...");
674          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
675            
676          contentEA =  (AlgorithmID)AlgorithmID.chacha20Poly1305.clone();
677          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
678          System.out.println("testing a S/MIME auth encrypted message for ChaChaPoly1305; decrypting for RSA user...");
679          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
680          System.out.println("testing a S/MIME auth encrypted message for ChaChaPoly1305; decrypting for ESDH user...");
681          testSMimeAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
682    
683          contentEA =  (AlgorithmID)AlgorithmID.aes128_GCM.clone();
684          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
685          System.out.println("testing a S/MIME signed and auth encrypted message for AES-128-GCM; decrypting for RSA user...");
686          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
687          System.out.println("testing a S/MIME signed and auth encrypted message for AES-128-GCM; decrypting for ESDH user...");
688          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
689          
690          contentEA =  (AlgorithmID)AlgorithmID.aes192_GCM.clone();
691          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
692          System.out.println("testing a S/MIME signed and auth encrypted message for AES-192-GCM; decrypting for RSA user...");
693          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
694          System.out.println("testing a S/MIME signed and auth encrypted message for AES-192-GCM; decrypting for ESDH user...");
695          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
696          
697          contentEA =  (AlgorithmID)AlgorithmID.aes256_GCM.clone();
698          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
699          System.out.println("testing a S/MIME signed and auth encrypted message for AES-256-GCM; decrypting for RSA user...");
700          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
701          System.out.println("testing a S/MIME signed and auth encrypted message for AES-256-GCM; decrypting for ESDH user...");
702          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
703          
704          contentEA =  (AlgorithmID)AlgorithmID.chacha20Poly1305.clone();
705          keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
706          System.out.println("testing a S/MIME signed and auth encrypted message for ChaChaPoly1305; decrypting for RSA user...");
707          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 0);
708          System.out.println("testing a S/MIME signed and auth encrypted message for ChaChaPoly1305; decrypting for ESDH user...");
709          testSMimeSignedAndAuthEncrypted(contentEA, keyWrapAlg, -1, 1);
710    
711          
712            } catch (Exception ex) {
713              ex.printStackTrace();
714              throw new RuntimeException(ex.toString());
715            }
716      }
717    
718      /**
719       * The main method.
720       * Reads the certificates and the private key and then starts the demos.
721       */
722      public static void main(String[] argv) throws IOException {
723    
724        DemoSMimeUtil.initDemos();
725        (new CMSStreamDemo()).start();
726    
727        System.out.println("\nReady!");
728        DemoUtil.waitKey();
729      }
730    
731      /**
732       * Inner class for copying data between the 2 streams.
733       */
734      static class Writer extends Thread {
735    
736        SMimeSigned signed;
737        OutputStream os;
738        Exception exception;
739    
740        public Writer(SMimeSigned signed, OutputStream os) {
741          super("Writer");
742          this.signed = signed;
743          this.os = os;
744        }
745    
746        /**
747         * Writes the SMimeSinged to the OutputStream.
748         */
749        public void run() {
750          try {
751            signed.writeTo(os);
752            os.close();
753          } catch (Exception ex) {
754            exception = ex;
755            System.out.println("Writer exception: "+exception);
756          }
757        }
758    
759        /**
760         * Returns a possible exception.
761         */
762        public Exception getException() {
763          return exception;
764        }
765      }
766    }