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/PKCS7CMSDigestedDataDemo.java 18    12.02.25 17:58 Dbratko $
059    // $Revision: 18 $
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.DigestedData;
068    import iaik.cms.DigestedDataStream;
069    import iaik.security.random.SecRandom;
070    import iaik.utils.Util;
071    
072    import java.io.ByteArrayInputStream;
073    import java.io.ByteArrayOutputStream;
074    import java.io.IOException;
075    import java.io.InputStream;
076    import java.security.NoSuchAlgorithmException;
077    import java.security.SecureRandom;
078    
079    import demo.DemoUtil;
080    
081    /**
082     * Compares the usage of IAIK CMS DigestedData(Stream) with IAIK PKSC#7 DigestedData(Stream).
083     */
084    public class PKCS7CMSDigestedDataDemo {
085    
086      // secure random number generator
087      SecureRandom random;
088    
089      /**
090       * Default constructor.
091       */
092      public PKCS7CMSDigestedDataDemo() throws IOException {
093        
094        System.out.println();
095        System.out.println("***********************************************************************************************");
096        System.out.println("*                                 PKCS7CMSDigestedDataDemo                                    *");
097        System.out.println("* (tests the CMS DigestedData against the IAIK-JCE PKCS#7 DigestedData type implementation)   *");
098        System.out.println("***********************************************************************************************");
099        System.out.println();
100    
101        random = SecRandom.getDefault();
102      }
103    
104    
105      /**
106       * Creates a CMS <code>DigestedData</code> object.
107       * <p>
108       * @param message the message to be digested, as byte representation
109       * @param mode IMPLICIT (include message) or EXPLICIT (do not include message)
110       * @return the DER encoding of the <code>DigestedData</code> object just created
111       * @throws CMSException if the <code>DigestedData</code> object cannot
112       *                          be created
113       * @throws IOException if an I/O error occurs
114       */
115      public byte[] createDigestedDataStream(byte[] message, int mode) throws CMSException, IOException  {
116    
117        System.out.println("Create a new message to be digested:");
118    
119        // we are testing the stream interface
120        ByteArrayInputStream is = new ByteArrayInputStream(message);
121    
122        // create a new DigestedData object which includes the data
123        DigestedDataStream digested_data = null;
124    
125        digested_data = new DigestedDataStream(is, (AlgorithmID)AlgorithmID.sha256.clone(), mode);
126    
127    
128        // write the data through DigestedData to any out-of-band place
129        if (mode == DigestedDataStream.EXPLICIT) {
130          InputStream data_is = digested_data.getInputStream();
131          byte[] buf = new byte[1024];
132          int r;
133          while ((r = data_is.read(buf)) > 0) {
134            ;   // skip data
135          }  
136        }
137    
138        // return the DigestedData as DER encoded byte array with block size 2048
139        ByteArrayOutputStream os = new ByteArrayOutputStream();
140        digested_data.writeTo(os, 2048);
141        return os.toByteArray();
142      }
143    
144    
145    
146      /**
147       * Parses a CMS <code>DigestedData</code> object and verifies the hash.
148       *
149       * @param digestedData <code>DigestedData</code> object as DER encoded byte array
150       * @param message the the message which was transmitted out-of-band
151       *
152       * @return the inherent message as byte array
153       * @throws CMSException if any signature does not verify
154       * @throws IOException if an I/O error occurs
155       */
156      public byte[] getDigestedDataStream(byte[] digestedData, byte[] message) throws CMSException, IOException {
157    
158        // we are testing the stream interface
159        ByteArrayInputStream is = new ByteArrayInputStream(digestedData);
160        // create the DigestedData object
161        DigestedDataStream digested_data = null;
162        if (message == null) {
163          // implicitly; read the DER encoded object
164          digested_data = new DigestedDataStream(is);
165        }
166        else {
167          // explicitly; set the data stream for digesting the message
168          digested_data = new DigestedDataStream(new ByteArrayInputStream(message), 
169                                                 (AlgorithmID)AlgorithmID.sha256.clone());
170        }
171    
172        // get an InputStream for reading the signed content
173        InputStream data = digested_data.getInputStream();
174        ByteArrayOutputStream os = new ByteArrayOutputStream();
175        Util.copyStream(data, os, null);
176    
177        if (message != null) {
178    
179          digested_data.decode(is);
180        }
181    
182        if (digested_data.verify()) {
183           System.out.println("Hash ok!");
184        } else {
185           System.out.println("Hash verification failed!");
186        }
187    
188        return os.toByteArray();
189      }
190    
191    
192      /**
193       * Creates a CMS <code>DigestedData</code> object.
194       * <p>
195       *
196       * @param message the message to be digested, as byte representation
197       * @param mode IMPLICIT (include message) or EXPLICIT (do not include message)
198       * @return the <code>DigestedData</code> as ASN.1 object
199       * @throws CMSException if the <code>DigestedData</code> object cannot
200       *                          be created
201       * @throws IOException if an I/O error occurs
202       */
203      public ASN1Object createDigestedData(byte[] message, int mode) throws CMSException, IOException  {
204    
205        System.out.println("Create a new digested message:");
206    
207        // create a new DigestedData object which includes the data
208        DigestedData digested_data = new DigestedData(message, (AlgorithmID)AlgorithmID.sha256.clone(), mode);
209    
210        return digested_data.toASN1Object();
211      }
212    
213      /**
214       * Parses a CMS <code>DigestedData</code> object and verifies the hash value.
215       *
216       * @param obj <code>DigestedData</code> object in ASN.1 representation
217       * @param message the the message which was transmitted out-of-band (explicit digested)
218       *
219       * @return the message
220       * @throws CMSException if some parsing exception occurs
221       * @throws IOException if an I/O error occurs
222       */
223      public byte[] getDigestedData(ASN1Object obj, byte[] message) throws CMSException, IOException {
224    
225        // create the DigestedData object
226        DigestedData digested_data = null;
227        if (message == null) {
228          // implicitly digested; read the ASN.1 object
229          digested_data = new DigestedData(obj);
230        }
231        else {
232          // explicitly digested; set the data for digesting the message
233          try {
234             digested_data = new DigestedData(message, (AlgorithmID)AlgorithmID.sha256.clone());
235             // if explicitly digested now the DER encoded object
236             digested_data.decode(obj);
237    
238          } catch (NoSuchAlgorithmException ex) {
239             throw new CMSException(ex.toString());
240          }
241        }
242    
243        // now verify the digest
244        if (digested_data.verify()) {
245           System.out.println("Hash ok!");
246        } else {
247           System.out.println("Hash verification failed!");
248        }
249    
250        return digested_data.getContent();
251      }
252      
253      //  PKCS#7
254      
255      /**
256       * Creates a PKCS#7 <code>DigestedData</code> object.
257       * <p>
258       * @param message the message to be digested, as byte representation
259       * @param mode IMPLICIT (include message) or EXPLICIT (do not include message)
260       * @return the DER encoding of the <code>DigestedData</code> object just created
261       * @throws iaik.pkcs.PKCSException if the <code>DigestedData</code> object cannot
262       *                          be created
263       * @throws IOException if an I/O error occurs
264       */
265      public byte[] createPKCS7DigestedDataStream(byte[] message, int mode) 
266        throws iaik.pkcs.PKCSException, IOException  {
267    
268        System.out.println("Create a new message to be digested:");
269    
270        // we are testing the stream interface
271        ByteArrayInputStream is = new ByteArrayInputStream(message);
272    
273        // create a new DigestedData object which includes the data
274        iaik.pkcs.pkcs7.DigestedDataStream digested_data = null;
275    
276        digested_data = new iaik.pkcs.pkcs7.DigestedDataStream(is, (AlgorithmID)AlgorithmID.sha256.clone(), mode);
277    
278    
279        // write the data through DigestedData to any out-of-band place
280        if (mode == iaik.pkcs.pkcs7.DigestedDataStream.EXPLICIT) {
281          InputStream data_is = digested_data.getInputStream();
282          byte[] buf = new byte[1024];
283          int r;
284          while ((r = data_is.read(buf)) > 0) {
285            ;   // skip data
286          }  
287        }
288    
289        // return the DigestedData as DER encoded byte array with block size 2048
290        ByteArrayOutputStream os = new ByteArrayOutputStream();
291        digested_data.writeTo(os, 2048);
292        return os.toByteArray();
293      }
294    
295    
296    
297      /**
298       * Parses a PKCS#7 <code>DigestedData</code> object and verifies the hash.
299       *
300       * @param digestedData <code>DigestedData</code> object as DER encoded byte array
301       * @param message the the message which was transmitted out-of-band
302       *
303       * @return the inherent message as byte array
304       * @throws iaik.pkcs.PKCSException if any signature does not verify
305       * @throws IOException if an I/O error occurs
306       */
307      public byte[] getPKCS7DigestedDataStream(byte[] digestedData, byte[] message) 
308        throws iaik.pkcs.PKCSException, IOException {
309    
310        // we are testing the stream interface
311        ByteArrayInputStream is = new ByteArrayInputStream(digestedData);
312        // create the DigestedData object
313        iaik.pkcs.pkcs7.DigestedDataStream digested_data = null;
314        if (message == null) {
315          // implicitly; read the DER encoded object
316          digested_data = new iaik.pkcs.pkcs7.DigestedDataStream(is);
317        }
318        else {
319          // explicitly; set the data stream for digesting the message
320          digested_data = new iaik.pkcs.pkcs7.DigestedDataStream(new ByteArrayInputStream(message),
321                                                                 (AlgorithmID)AlgorithmID.sha256.clone());
322        }
323    
324        // get an InputStream for reading the signed content
325        InputStream data = digested_data.getInputStream();
326        ByteArrayOutputStream os = new ByteArrayOutputStream();
327        Util.copyStream(data, os, null);
328    
329        if (message != null) {
330    
331          digested_data.decode(is);
332        }
333    
334        if (digested_data.verify()) {
335           System.out.println("Hash ok!");
336        } else {
337           System.out.println("Hash verification failed!");
338        }
339    
340        return os.toByteArray();
341      }
342    
343    
344      /**
345       * Creates a PKCS#7 <code>DigestedData</code> object.
346       * <p>
347       *
348       * @param message the message to be digested, as byte representation
349       * @param mode IMPLICIT (include message) or EXPLICIT (do not include message)
350       * @return the <code>DigestedData</code> as ASN.1 object
351       * @throws iaik.pkcs.PKCSException if the <code>DigestedData</code> object cannot
352       *                          be created
353       * @throws IOException if an I/O error occurs
354       */
355      public ASN1Object createPKCS7DigestedData(byte[] message, int mode) 
356        throws iaik.pkcs.PKCSException, IOException  {
357    
358        System.out.println("Create a new digested message:");
359    
360        // create a new DigestedData object which includes the data
361        iaik.pkcs.pkcs7.DigestedData digested_data = new iaik.pkcs.pkcs7.DigestedData(message, 
362                                                                                      (AlgorithmID)AlgorithmID.sha256.clone(),
363                                                                                      mode);
364    
365        return digested_data.toASN1Object();
366      }
367    
368      /**
369       * Parses a PKCS#7 <code>DigestedData</code> object and verifies the hash value.
370       *
371       * @param obj <code>DigestedData</code> object in ASN.1 representation
372       * @param message the the message which was transmitted out-of-band (explicit digested)
373       *
374       * @return the message
375       * @throws iaik.pkcs.PKCSException if some parsing exception occurs
376       * @throws IOException if an I/O error occurs
377       */
378      public byte[] getPKCS7DigestedData(ASN1Object obj, byte[] message) 
379        throws iaik.pkcs.PKCSException, IOException {
380    
381        // create the DigestedData object
382        iaik.pkcs.pkcs7.DigestedData digested_data = null;
383        if (message == null) {
384          // implicitly digested; read the ASN.1 object
385          digested_data = new iaik.pkcs.pkcs7.DigestedData(obj);
386        }
387        else {
388          // explicitly digested; set the data for digesting the message
389          try {
390             digested_data = new iaik.pkcs.pkcs7.DigestedData(message, (AlgorithmID)AlgorithmID.sha256.clone());
391             // if explicitly digested now the DER encoded object
392             digested_data.decode(obj);
393    
394          } catch (NoSuchAlgorithmException ex) {
395             throw new iaik.pkcs.PKCSException(ex.toString());
396          }
397        }
398    
399        // now verify the digest
400        if (digested_data.verify()) {
401           System.out.println("Hash ok!");
402        } else {
403           System.out.println("Hash verification failed!");
404        }
405    
406        return digested_data.getContent();
407      }
408    
409    
410    
411    
412      /**
413       * Starts the tests.
414       */
415      public void start() {
416         // the test message
417        String m = "This is the test message.";
418        System.out.println("Test message: \""+m+"\"");
419        System.out.println();
420        byte[] message = m.getBytes();
421    
422        try {
423          byte[] data;
424          byte[] received_message = null;
425          System.out.println("Stream implementation demos");
426          System.out.println("===========================");
427    
428          // the stream implementation
429       
430          //
431          // test CMS Implicit DigestedDataStream
432          //
433          System.out.println("\nImplicit DigestedDataStream demo [create]:\n");
434          data = createDigestedDataStream(message, DigestedDataStream.IMPLICIT);
435          // transmit data
436          System.out.println("\nImplicit DigestedDataStream demo [parse]:\n");
437          received_message = getDigestedDataStream(data, null);
438          System.out.print("\nContent: ");
439          System.out.println(new String(received_message));
440    
441          //
442          // test CMS Explicit DigestedDataStream
443          //
444          System.out.println("\nExplicit DigestedDataStream demo [create]:\n");
445          data = createDigestedDataStream(message, DigestedDataStream.EXPLICIT);
446          // transmit data
447          System.out.println("\nExplicit DigestedDataStream demo [parse]:\n");
448          received_message = getDigestedDataStream(data, message);
449          System.out.print("\nContent: ");
450          System.out.println(new String(received_message));
451          
452          System.out.println("Testing compatiblity to PKCS#7...");
453          
454          System.out.println("\nCMS Implicit DigestedDataStream demo [create]:\n");
455          data = createDigestedDataStream(message, DigestedDataStream.IMPLICIT);
456          // transmit data
457          System.out.println("\nPKCS#7 Implicit DigestedDataStream demo [parse]:\n");
458          received_message = getPKCS7DigestedDataStream(data, null);
459          System.out.print("\nContent: ");
460          System.out.println(new String(received_message));
461          
462          System.out.println("\nPKCS#7 Implicit DigestedDataStream demo [create]:\n");
463          data = createPKCS7DigestedDataStream(message, DigestedDataStream.IMPLICIT);
464          // transmit data
465          System.out.println("\nCMS Implicit DigestedDataStream demo [parse]:\n");
466          received_message = getDigestedDataStream(data, null);
467          System.out.print("\nContent: ");
468          System.out.println(new String(received_message));
469    
470          //
471          // test CMS Explicit DigestedDataStream
472          //
473          System.out.println("\nCMS Explicit DigestedDataStream demo [create]:\n");
474          data = createDigestedDataStream(message, DigestedDataStream.EXPLICIT);
475          // transmit data
476          System.out.println("\nPKCS#7 Explicit DigestedDataStream demo [parse]:\n");
477          received_message = getPKCS7DigestedDataStream(data, message);
478          System.out.print("\nContent: ");
479          System.out.println(new String(received_message));
480          
481          System.out.println("\nPKCS#7 Explicit DigestedDataStream demo [create]:\n");
482          data = createPKCS7DigestedDataStream(message, DigestedDataStream.EXPLICIT);
483          // transmit data
484          System.out.println("\nCMS Explicit DigestedDataStream demo [parse]:\n");
485          received_message = getDigestedDataStream(data, message);
486          System.out.print("\nContent: ");
487          System.out.println(new String(received_message));
488    
489    
490       
491          // the non-stream implementation
492          System.out.println("\nNon-stream implementation demos");
493          System.out.println("===============================");
494    
495          ASN1Object obj = null;
496    
497          //
498          // test CMS Implicit DigestedData
499          //
500          System.out.println("\nImplicit DigestedData demo [create]:\n");
501          obj = createDigestedData(message, DigestedData.IMPLICIT);
502          // transmit data
503          System.out.println("\nImplicit DigestedData demo [parse]:\n");
504          received_message = getDigestedData(obj, null);
505          System.out.print("\nContent: ");
506          System.out.println(new String(received_message));
507    
508          //
509          // test CMS Explicit DigestedData
510          //
511          System.out.println("\nExplicit DigestedData demo [create]:\n");
512          obj = createDigestedData(message, DigestedData.EXPLICIT);
513          // transmit data
514          System.out.println("\nExplicit DigestedData demo [parse]:\n");
515          received_message = getDigestedData(obj, message);
516          System.out.print("\nContent: ");
517          System.out.println(new String(received_message));
518          
519          /// CMS <---> PKCS#7
520          System.out.println("Testing compatibility to PKCS#7...");
521          
522          //
523          // test PKCS#7 Implicit DigestedData
524          //
525          System.out.println("\nCMS Implicit DigestedData demo [create]:\n");
526          obj = createDigestedData(message, DigestedData.IMPLICIT);
527          // transmit data
528          System.out.println("\nPKCS#7 Implicit DigestedData demo [parse]:\n");
529          received_message = getPKCS7DigestedData(obj, null);
530          System.out.print("\nContent: ");
531          System.out.println(new String(received_message));
532          
533          System.out.println("\nPKCS#7 Implicit DigestedData demo [create]:\n");
534          obj = createPKCS7DigestedData(message, DigestedData.IMPLICIT);
535          // transmit data
536          System.out.println("\nCMS Implicit DigestedData demo [parse]:\n");
537          received_message = getDigestedData(obj, null);
538          System.out.print("\nContent: ");
539          System.out.println(new String(received_message));
540    
541    
542          //
543          // test PKCS#7 Explicit DigestedData
544          //
545          System.out.println("\nCMS Explicit DigestedData demo [create]:\n");
546          obj = createDigestedData(message, DigestedData.EXPLICIT);
547          // transmit data
548          System.out.println("\nPKCS#7 Explicit DigestedData demo [parse]:\n");
549          received_message = getPKCS7DigestedData(obj, message);
550          System.out.print("\nContent: ");
551          System.out.println(new String(received_message));
552          
553          System.out.println("\nPKCS7 Explicit DigestedData demo [create]:\n");
554          obj = createPKCS7DigestedData(message, DigestedData.EXPLICIT);
555          // transmit data
556          System.out.println("\nCMS Explicit DigestedData demo [parse]:\n");
557          received_message = getDigestedData(obj, message);
558          System.out.print("\nContent: ");
559          System.out.println(new String(received_message));
560     
561            } catch (Exception ex) {
562              ex.printStackTrace();
563              throw new RuntimeException(ex.toString());
564            }
565      }
566    
567      /**
568       * Tests the IAIK CMS DigestedData(Stream) implementation against
569       * the PKCS#7 DigestedData(Stream) implementation
570       */
571      public static void main(String argv[]) throws Exception {
572    
573            DemoUtil.initDemos();
574    
575        (new PKCS7CMSDigestedDataDemo()).start();
576        System.out.println("\nReady!");
577        DemoUtil.waitKey();
578      }
579    }