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