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/pkcs7cms/PKCS7CMSEncryptedDataDemo.java 16    12.02.25 17:58 Dbratko $
059    // $Revision: 16 $
060    //
061    
062    package demo.cms.pkcs7cms;
063    
064    import iaik.asn1.ASN1Object;
065    import iaik.asn1.structures.AlgorithmID;
066    import iaik.cms.CMSException;
067    import iaik.cms.EncryptedContentInfo;
068    import iaik.cms.EncryptedContentInfoStream;
069    import iaik.cms.EncryptedData;
070    import iaik.cms.EncryptedDataStream;
071    import iaik.security.random.SecRandom;
072    import iaik.utils.Util;
073    
074    import java.io.ByteArrayInputStream;
075    import java.io.ByteArrayOutputStream;
076    import java.io.IOException;
077    import java.io.InputStream;
078    import java.security.InvalidAlgorithmParameterException;
079    import java.security.InvalidKeyException;
080    import java.security.NoSuchAlgorithmException;
081    import java.security.SecureRandom;
082    import java.security.spec.InvalidParameterSpecException;
083    
084    import demo.DemoUtil;
085    
086    /**
087     * Compares the usage of IAIK CMS with the IAIK PKCS#7 EncryptedData(Stream) implementation.
088     */
089    public class PKCS7CMSEncryptedDataDemo {
090    
091      // secure random number generator
092      SecureRandom random;
093    
094      /**
095       * Default constructor.
096       */
097      public PKCS7CMSEncryptedDataDemo() {
098        
099        System.out.println();
100        System.out.println("***********************************************************************************************");
101        System.out.println("*                                 PKCS7CMSEncryptedDataDemo                                   *");
102        System.out.println("* (tests the CMS EncryptedData against the IAIK-JCE PKCS#7 EncryptedData type implementation) *");
103        System.out.println("***********************************************************************************************");
104        System.out.println();
105        
106        random = SecRandom.getDefault();
107      }
108    
109    
110      /**
111       * Creates a CMS <code>EncryptedDataStream</code> message.
112       * <p>
113       * The supplied content is PBE-encrypted using the specified password.
114       *
115       * @param message the message to be encrypted, as byte representation
116       * @param pbeAlgorithm the PBE algorithm to be used
117       * @param password the password
118       * @return the DER encoding of the <code>EncryptedData</code> object just created
119       * @throws CMSException if the <code>EncryptedData</code> object cannot
120       *                          be created
121       * @throws IOException if an I/O error occurs
122       */
123      public byte[] createEncryptedDataStream(byte[] message, AlgorithmID pbeAlgorithm, char[] password) throws CMSException, IOException {
124    
125        EncryptedDataStream encrypted_data;
126    
127        // we are testing the stream interface
128        ByteArrayInputStream is = new ByteArrayInputStream(message);
129        // create a new EnvelopedData object encrypted with TripleDES CBC
130        try {
131          encrypted_data = new EncryptedDataStream(is, 2048);
132          encrypted_data.setupCipher(pbeAlgorithm, password);
133        } catch (InvalidKeyException ex) {
134          throw new CMSException("Key error: "+ex.toString());
135        } catch (NoSuchAlgorithmException ex) {
136          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
137        }
138    
139        // return the EnvelopedDate as DER encoded byte array with block size 2048
140        ByteArrayOutputStream os = new ByteArrayOutputStream();
141        encrypted_data.writeTo(os);
142        return os.toByteArray();
143      }
144    
145      /**
146       * Decrypts the PBE-encrypted content of the given CMS <code>EncryptedData</code> object
147       * using the specified password and returns the decrypted (= original) message.
148       *
149       * @param encoding the <code>EncryptedData</code> object as DER encoded byte array
150       * @param password the password to decrypt the message
151       *
152       * @return the recovered message, as byte array
153       * @throws CMSException if the message cannot be recovered
154       * @throws IOException if an I/O error occurs
155       */
156      public byte[] getEncryptedDataStream(byte[] encoding, char[] password) throws CMSException, IOException {
157    
158        // create the EncryptpedData object from a DER encoded byte array
159        // we are testing the stream interface
160        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
161        EncryptedDataStream encrypted_data = new EncryptedDataStream(is);
162    
163        System.out.println("Information about the encrypted data:");
164        EncryptedContentInfoStream eci = encrypted_data.getEncryptedContentInfo();
165        System.out.println("Content type: "+eci.getContentType().getName());
166        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
167    
168        // decrypt the message
169        try {
170          encrypted_data.setupCipher(password);
171          InputStream decrypted = encrypted_data.getInputStream();
172          ByteArrayOutputStream os = new ByteArrayOutputStream();
173          Util.copyStream(decrypted, os, null);
174    
175          return os.toByteArray();
176    
177        } catch (InvalidKeyException ex) {
178          throw new CMSException("Key error: "+ex.toString());
179        } catch (NoSuchAlgorithmException ex) {
180          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
181        } catch (InvalidAlgorithmParameterException ex) {
182          throw new CMSException("Invalid Parameters: "+ex.toString());
183        } catch (InvalidParameterSpecException ex) {
184          throw new CMSException("Invalid Parameters: "+ex.toString());
185        }
186      }
187    
188      /**
189       * Creates a CMS <code>EncryptedData</code> message.
190       * <p>
191       * The supplied content is PBE-encrypted using the specified password.
192       *
193       * @param message the message to be encrypted, as byte representation
194       * @param pbeAlgorithm the PBE algorithm to be used
195       * @param password the password
196       * @return the DER encoding of the <code>EncryptedData</code> object just created
197       * @throws CMSException if the <code>EncryptedData</code> object cannot
198       *                          be created
199       * @throws IOException if an I/O error occurs
200       */
201      public ASN1Object createEncryptedData(byte[] message, AlgorithmID pbeAlgorithm, char[] password) throws CMSException, IOException {
202    
203        EncryptedData encrypted_data;
204    
205        try {
206          encrypted_data = new EncryptedData(message);
207          // encrypt the message
208          encrypted_data.setupCipher(pbeAlgorithm, password);
209        } catch (InvalidKeyException ex) {
210          throw new CMSException("Key error: "+ex.toString());
211        } catch (NoSuchAlgorithmException ex) {
212          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
213        }
214        return encrypted_data.toASN1Object();
215    
216      }
217    
218      /**
219       * Decrypts the PBE-encrypted content of the given CMS <code>EncryptedData</code> object
220       * using the specified password and returns the decrypted (= original) message.
221       *
222       * @param asn1Object the <code>EncryptedData</code> object as ASN1Object
223       * @param password the password to decrypt the message
224       *
225       * @return the recovered message, as byte array
226       * @throws CMSException if the message cannot be recovered
227       * @throws IOException if an I/O error occurs
228       */
229      public byte[] getEncryptedData(ASN1Object asn1Object, char[] password) throws CMSException, IOException {
230    
231        // create an EncryptedData from the ASN1Object
232        EncryptedData encrypted_data = new EncryptedData(asn1Object);
233    
234        System.out.println("Information about the encrypted data:");
235        EncryptedContentInfo eci = (EncryptedContentInfo)encrypted_data.getEncryptedContentInfo();
236        System.out.println("Content type: "+eci.getContentType().getName());
237        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
238    
239        // decrypt the message
240        try {
241          encrypted_data.setupCipher(password);
242          return encrypted_data.getContent();
243    
244        } catch (InvalidKeyException ex) {
245          throw new CMSException("Key error: "+ex.toString());
246        } catch (NoSuchAlgorithmException ex) {
247          throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
248        } catch (InvalidAlgorithmParameterException ex) {
249          throw new CMSException("Invalid Parameters: "+ex.toString());
250        } catch (InvalidParameterSpecException ex) {
251          throw new CMSException("Invalid Parameters: "+ex.toString());
252        }
253      }
254      
255      // PKCS#7
256      
257      
258      /**
259       * Creates a PKCS#7 <code>EncryptedDataStream</code> message.
260       * <p>
261       * The supplied content is PBE-encrypted using the specified password.
262       *
263       * @param message the message to be encrypted, as byte representation
264       * @param pbeAlgorithm the PBE algorithm to be used
265       * @param password the password
266       * @return the DER encoding of the <code>EncryptedData</code> object just created
267       * @throws iaik.pkcs.PKCSException if the <code>EncryptedData</code> object cannot
268       *                          be created
269       * @throws IOException if an I/O error occurs
270       */
271      public byte[] createPKCS7EncryptedDataStream(byte[] message, AlgorithmID pbeAlgorithm, char[] password) 
272        throws iaik.pkcs.PKCSException, IOException {
273    
274        iaik.pkcs.pkcs7.EncryptedDataStream encrypted_data;
275    
276        // we are testing the stream interface
277        ByteArrayInputStream is = new ByteArrayInputStream(message);
278        // create a new EnvelopedData object encrypted with TripleDES CBC
279        try {
280          encrypted_data = new iaik.pkcs.pkcs7.EncryptedDataStream(is, 2048);
281          encrypted_data.setupCipher(pbeAlgorithm, password);
282        } catch (InvalidKeyException ex) {
283          throw new iaik.pkcs.PKCSException("Key error: "+ex.toString());
284        } catch (NoSuchAlgorithmException ex) {
285          throw new iaik.pkcs.PKCSException("Content encryption algorithm not implemented: "+ex.getMessage());
286        }
287    
288        // return the EnvelopedDate as DER encoded byte array with block size 2048
289        ByteArrayOutputStream os = new ByteArrayOutputStream();
290        encrypted_data.writeTo(os);
291        return os.toByteArray();
292      }
293    
294      /**
295       * Decrypts the PBE-encrypted content of the given PKCS#7 <code>EncryptedData</code> object
296       * using the specified password and returns the decrypted (= original) message.
297       *
298       * @param encoding the <code>EncryptedData</code> object as DER encoded byte array
299       * @param password the password to decrypt the message
300       *
301       * @return the recovered message, as byte array
302       * @throws iaik.pkcs.PKCSException if the message cannot be recovered
303       * @throws IOException if an I/O error occurs
304       */
305      public byte[] getPKCS7EncryptedDataStream(byte[] encoding, char[] password) 
306        throws iaik.pkcs.PKCSException, IOException {
307    
308        // create the EncryptpedData object from a DER encoded byte array
309        // we are testing the stream interface
310        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
311        iaik.pkcs.pkcs7.EncryptedDataStream encrypted_data = new iaik.pkcs.pkcs7.EncryptedDataStream(is);
312    
313        System.out.println("Information about the encrypted data:");
314        iaik.pkcs.pkcs7.EncryptedContentInfoStream eci = (iaik.pkcs.pkcs7.EncryptedContentInfoStream)encrypted_data.getEncryptedContentInfo();
315        System.out.println("Content type: "+eci.getContentType().getName());
316        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
317    
318        // decrypt the message
319        try {
320          encrypted_data.setupCipher(password);
321          InputStream decrypted = encrypted_data.getInputStream();
322          ByteArrayOutputStream os = new ByteArrayOutputStream();
323          Util.copyStream(decrypted, os, null);
324    
325          return os.toByteArray();
326    
327        } catch (InvalidKeyException ex) {
328          throw new iaik.pkcs.PKCSException("Key error: "+ex.toString());
329        } catch (NoSuchAlgorithmException ex) {
330          throw new iaik.pkcs.PKCSException("Content encryption algorithm not implemented: "+ex.getMessage());
331        } catch (InvalidAlgorithmParameterException ex) {
332          throw new iaik.pkcs.PKCSException("Invalid Parameters: "+ex.toString());
333        } catch (InvalidParameterSpecException ex) {
334          throw new iaik.pkcs.PKCSException("Invalid Parameters: "+ex.toString());
335        }
336      }
337    
338      /**
339       * Creates a PKCS#7 <code>EncryptedData</code> message.
340       * <p>
341       * The supplied content is PBE-encrypted using the specified password.
342       *
343       * @param message the message to be encrypted, as byte representation
344       * @param pbeAlgorithm the PBE algorithm to be used
345       * @param password the password
346       * @return the DER encoding of the <code>EncryptedData</code> object just created
347       * @throws iaik.pkcs.PKCSException if the <code>EncryptedData</code> object cannot
348       *                          be created
349       * @throws IOException if an I/O error occurs
350       */
351      public ASN1Object createPKCS7EncryptedData(byte[] message, AlgorithmID pbeAlgorithm, char[] password) 
352        throws iaik.pkcs.PKCSException, IOException {
353    
354        iaik.pkcs.pkcs7.EncryptedData encrypted_data;
355    
356        try {
357          encrypted_data = new iaik.pkcs.pkcs7.EncryptedData(message);
358          // encrypt the message
359          encrypted_data.setupCipher(pbeAlgorithm, password);
360        } catch (InvalidKeyException ex) {
361          throw new iaik.pkcs.PKCSException("Key error: "+ex.toString());
362        } catch (NoSuchAlgorithmException ex) {
363          throw new iaik.pkcs.PKCSException("Content encryption algorithm not implemented: "+ex.getMessage());
364        }
365        return encrypted_data.toASN1Object();
366    
367      }
368    
369      /**
370       * Decrypts the PBE-encrypted content of the given PKCS#7 <code>EncryptedData</code> object
371       * using the specified password and returns the decrypted (= original) message.
372       *
373       * @param asn1Object the <code>EncryptedData</code> object as ASN1Object
374       * @param password the password to decrypt the message
375       *
376       * @return the recovered message, as byte array
377       * @throws iaik.pkcs.PKCSException if the message cannot be recovered
378       * @throws IOException if an I/O error occurs
379       */
380      public byte[] getPKCS7EncryptedData(ASN1Object asn1Object, char[] password) 
381        throws iaik.pkcs.PKCSException, IOException {
382    
383        // create an EncryptedData from the ASN1Object
384        iaik.pkcs.pkcs7.EncryptedData encrypted_data = new iaik.pkcs.pkcs7.EncryptedData(asn1Object);
385    
386        System.out.println("Information about the encrypted data:");
387        iaik.pkcs.pkcs7.EncryptedContentInfo eci = (iaik.pkcs.pkcs7.EncryptedContentInfo)encrypted_data.getEncryptedContentInfo();
388        System.out.println("Content type: "+eci.getContentType().getName());
389        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
390    
391        // decrypt the message
392        try {
393          encrypted_data.setupCipher(password);
394          return encrypted_data.getContent();
395    
396        } catch (InvalidKeyException ex) {
397          throw new iaik.pkcs.PKCSException("Key error: "+ex.toString());
398        } catch (NoSuchAlgorithmException ex) {
399          throw new iaik.pkcs.PKCSException("Content encryption algorithm not implemented: "+ex.getMessage());
400        } catch (InvalidAlgorithmParameterException ex) {
401          throw new iaik.pkcs.PKCSException("Invalid Parameters: "+ex.toString());
402        } catch (InvalidParameterSpecException ex) {
403          throw new iaik.pkcs.PKCSException("Invalid Parameters: "+ex.toString()); 
404        }
405      }
406    
407    
408      /**
409       * Starts the tests.
410       */
411      public void start() {
412         // the test message
413        String m = "This is the test message.";
414        System.out.println("Test message: \""+m+"\"");
415        System.out.println();
416        byte[] message = m.getBytes();
417    
418        try {
419          byte[] data;
420          byte[] received_message = null;
421          System.out.println("Stream implementation demos");
422          System.out.println("===========================");
423    
424    
425          //
426          // test CMS EncryptedDataStream
427          //
428          System.out.println("\nEncryptedDataStream demo [create]:\n");
429          data = createEncryptedDataStream(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
430          // transmit data
431          System.out.println("\nEncryptedDataStream demo [parse]:\n");
432          received_message = getEncryptedDataStream(data, "password".toCharArray());
433          System.out.print("\nContent: ");
434          System.out.println(new String(received_message));
435    
436          System.out.println("Testing compatibility to PKCS#7...");
437          
438          System.out.println("\nCMS EncryptedDataStream demo [create]:\n");
439          data = createEncryptedDataStream(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
440          // transmit data
441          System.out.println("\nPKCS#7 EncryptedDataStream demo [parse]:\n");
442          received_message = getPKCS7EncryptedDataStream(data, "password".toCharArray());
443          System.out.print("\nContent: ");
444          System.out.println(new String(received_message));
445          
446          System.out.println("\nPKCS7 EncryptedDataStream demo [create]:\n");
447          data = createPKCS7EncryptedDataStream(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
448          // transmit data
449          System.out.println("\nCMS EncryptedDataStream demo [parse]:\n");
450          received_message = getEncryptedDataStream(data, "password".toCharArray());
451          System.out.print("\nContent: ");
452          System.out.println(new String(received_message));
453    
454    
455          // the non-stream implementation
456          System.out.println("\nNon-stream implementation demos");
457          System.out.println("===============================");
458    
459          ASN1Object obj = null;
460    
461    
462          //
463          // test CMS EncryptedData
464          //
465          System.out.println("\nEncryptedData demo [create]:\n");
466          obj = createEncryptedData(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
467          // transmit data
468          System.out.println("\nEncryptedData demo [parse]:\n");
469          received_message = getEncryptedData(obj, "password".toCharArray());
470          System.out.print("\nContent: ");
471          System.out.println(new String(received_message));
472          
473          System.out.println("Testing compatibility to PKCS#7...");
474          
475          System.out.println("\nCMS EncryptedData demo [create]:\n");
476          obj = createEncryptedData(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
477          // transmit data
478          System.out.println("\nPKCS#7 EncryptedData demo [parse]:\n");
479          received_message = getPKCS7EncryptedData(obj, "password".toCharArray());
480          System.out.print("\nContent: ");
481          System.out.println(new String(received_message));
482          
483          System.out.println("\nPKCS#7 EncryptedData demo [create]:\n");
484          obj = createPKCS7EncryptedData(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
485          // transmit data
486          System.out.println("\nCMS EncryptedData demo [parse]:\n");
487          received_message = getEncryptedData(obj, "password".toCharArray());
488          System.out.print("\nContent: ");
489          System.out.println(new String(received_message));
490    
491            } catch (Exception ex) {
492              ex.printStackTrace();
493              throw new RuntimeException(ex.toString());
494            }
495      }
496    
497      /**
498       * Tests the IAIK CMS against the IAIK PKCS#7 EncryptedData(Stream) implementation.
499       */
500      public static void main(String argv[]) throws Exception {
501    
502        DemoUtil.initDemos();
503    
504        (new PKCS7CMSEncryptedDataDemo()).start();
505        System.out.println("\nReady!");
506        DemoUtil.waitKey();
507      }
508    }