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/signedData/SHA2withDSASignedDataDemo.java 5     12.02.25 17:58 Dbratko $
059    // $Revision: 5 $
060    //
061    
062    package demo.cms.signedData;
063    
064    import iaik.asn1.ObjectID;
065    import iaik.asn1.structures.AlgorithmID;
066    import iaik.asn1.structures.Attribute;
067    import iaik.cms.CMSException;
068    import iaik.cms.IssuerAndSerialNumber;
069    import iaik.cms.SignedData;
070    import iaik.cms.SignedDataStream;
071    import iaik.cms.SignerInfo;
072    import iaik.cms.attributes.CMSContentType;
073    import iaik.cms.attributes.SigningTime;
074    import iaik.utils.Util;
075    import iaik.x509.X509Certificate;
076    
077    import java.io.ByteArrayInputStream;
078    import java.io.ByteArrayOutputStream;
079    import java.io.IOException;
080    import java.io.InputStream;
081    import java.security.NoSuchAlgorithmException;
082    import java.security.PrivateKey;
083    import java.security.SignatureException;
084    
085    import demo.DemoUtil;
086    import demo.keystore.CMSKeyStore;
087    
088    
089    /**
090     * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and
091     * {@link iaik.cms.SignedData} for signing some data using the CMS type
092     * SignedData with SHA2withDSA signature algorithm according to FIPS 186-3.
093     */
094    public class SHA2withDSASignedDataDemo {
095    
096      // The private key of the signer.
097      PrivateKey signerKey_;
098    
099      // The certificate chain of the signer.
100      X509Certificate[] signerCertificates_;
101    
102    
103      
104      /**
105       * Setups the demo certificate chains.
106       * 
107       * Keys and certificate are retrieved from the demo KeyStore.
108       * 
109       * @throws IOException if an file read error occurs
110       */
111      public SHA2withDSASignedDataDemo() throws IOException {
112        
113        System.out.println();
114        System.out.println("**********************************************************************************");
115        System.out.println("*                        SHA2withDSASignedDataDemo                               *");
116        System.out.println("*       (shows the usage of the CMS SignedData type with SHA2withDSA)            *");
117        System.out.println("**********************************************************************************");
118        System.out.println();
119        
120        // add all certificates to the list
121        signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
122        signerKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
123      }
124      
125      /**
126       * Creates a CMS <code>SignedData</code> object.
127       * <p>
128       *
129       * @param message the message to be signed, as byte representation
130       * @param mode the transmission mode, either IMPLICIT or EXPLICIT
131       * @return the BER encoding of the <code>SignedData</code> object just created
132       * @throws CMSException if the <code>SignedData</code> object cannot
133       *                          be created
134       * @throws IOException if some stream I/O error occurs
135       */
136      public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException  {
137        
138        System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN());
139       
140        // we are testing the stream interface
141        ByteArrayInputStream is = new ByteArrayInputStream(message);
142        // create a new SignedData object which includes the data
143        SignedDataStream signed_data = new SignedDataStream(is, mode);
144        
145        // SignedData shall include the certificate chain for verifying
146        signed_data.setCertificates(signerCertificates_);
147        
148        // cert at index 0 is the signer certificate
149        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]);
150    
151        // create a new SignerInfo
152        SignerInfo signer_info = new SignerInfo(issuer,
153                                                (AlgorithmID)AlgorithmID.sha256.clone(),
154                                                (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
155                                                signerKey_);
156        // create some authenticated attributes
157        // the message digest attribute is automatically added
158        Attribute[] attributes = new Attribute[2];
159        try {
160          // content type is data
161          CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
162          attributes[0] = new Attribute(contentType);
163          // signing time is now
164          SigningTime signingTime = new SigningTime();
165          attributes[1] = new Attribute(signingTime);
166          
167        } catch (Exception ex) {
168          throw new CMSException("Error creating attribute: " + ex.toString());   
169        }    
170        // set the attributes
171        signer_info.setSignedAttributes(attributes);
172        // finish the creation of SignerInfo by calling method addSigner
173        try {
174          signed_data.addSignerInfo(signer_info);
175        } catch (NoSuchAlgorithmException ex) {
176          throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
177        } 
178    
179        // write the data through SignedData to any out-of-band place
180        if (mode == SignedDataStream.EXPLICIT) {
181          InputStream data_is = signed_data.getInputStream();
182          byte[] buf = new byte[1024];
183          int r;
184          while ((r = data_is.read(buf)) > 0) {
185            ;   // skip data
186          }  
187        }
188    
189        // return the SignedData as DER encoded byte array with block size 2048
190        ByteArrayOutputStream os = new ByteArrayOutputStream();
191        signed_data.writeTo(os, 2048);
192        return os.toByteArray();
193      }
194      
195    
196      /**
197       * Parses a CMS <code>SignedData</code> object and verifies the signatures
198       * for all participated signers.
199       *
200       * @param signedData <code>SignedData</code> object as BER encoded byte array
201       * @param message the the message which was transmitted out-of-band (explicit signed)
202       *
203       * @return the inherent message as byte array
204       * @throws CMSException if any signature does not verify
205       * @throws IOException if some stream I/O error occurs
206       */
207      public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException {
208    
209        // we are testing the stream interface
210        ByteArrayInputStream is = new ByteArrayInputStream(signedData);
211        // create the SignedData object
212        SignedDataStream signed_data = new SignedDataStream(is);
213        
214        if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
215          // in explicit mode explicitly supply the content for hash computation  
216          signed_data.setInputStream(new ByteArrayInputStream(message));
217        }
218    
219        // get an InputStream for reading the signed content
220        InputStream data = signed_data.getInputStream();
221        ByteArrayOutputStream os = new ByteArrayOutputStream();
222        Util.copyStream(data, os, null);
223        
224        System.out.println("SignedData contains the following signer information:");
225        SignerInfo[] signer_infos = signed_data.getSignerInfos();
226        
227        int numberOfSignerInfos = signer_infos.length;
228        if (numberOfSignerInfos == 0) {
229          String warning = "Warning: Unsigned message (no SignerInfo included)!";  
230          System.err.println(warning);
231          throw new CMSException(warning);
232        } else {
233          for (int i = 0; i < numberOfSignerInfos; i++) {
234            AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm();  
235            try {
236              // verify the signed data using the SignerInfo at index i
237              X509Certificate signer_cert = signed_data.verify(i);
238              // if the signature is OK the certificate of the signer is returned
239              System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN());
240              SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
241              if (signingTime != null) {
242                System.out.println("This message has been signed at " + signingTime.get());
243              } 
244              CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
245              if (contentType != null) {
246                System.out.println("The content has CMS content type " + contentType.get().getName());
247              }
248            } catch (SignatureException ex) {
249              // if the signature is not OK a SignatureException is thrown
250              System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
251              throw new CMSException(ex.toString());
252            }  
253          }  
254        
255          // now check alternative signature verification
256          System.out.println("Now check the signature assuming that no certs have been included:");
257          try {
258             SignerInfo signer_info = signed_data.verify(signerCertificates_[0]);
259             AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm();
260             // if the signature is OK the certificate of the signer is returned
261             System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signerCertificates_[0].getSubjectDN());
262              
263          } catch (SignatureException ex) {
264              // if the signature is not OK a SignatureException is thrown
265              System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN());
266              throw new CMSException(ex.toString());
267          }
268          // in practice we also would validate the signer certificate(s)  
269        }
270        return os.toByteArray();
271      }
272      
273      
274      
275      /**
276       * Creates a CMS <code>SignedData</code> object.
277       * <p>
278       *
279       * @param message the message to be signed, as byte representation
280       * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
281       * @return the DER encoded <code>SignedData</code> object
282       * @throws CMSException if the <code>SignedData</code> object cannot
283       *                          be created
284       */
285      public byte[] createSignedData(byte[] message, int mode) throws CMSException {
286        
287        System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN());
288      
289        // create a new SignedData object which includes the data
290        SignedData signed_data = new SignedData(message, mode);
291        
292        // SignedData shall include the certificate chain for verifying
293        signed_data.setCertificates(signerCertificates_);
294      
295        // cert at index 0 is the signer certificate
296        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]);
297    
298        // create a new SignerInfo
299        SignerInfo signer_info = new SignerInfo(issuer,
300                                                (AlgorithmID)AlgorithmID.sha256.clone(),
301                                                (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
302                                                signerKey_);
303        // create some authenticated attributes
304        // the message digest attribute is automatically added
305        Attribute[] attributes = new Attribute[2];
306        try {
307          // content type is data
308          CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
309          attributes[0] = new Attribute(contentType);
310          // signing time is now
311          SigningTime signingTime = new SigningTime();
312          attributes[1] = new Attribute(signingTime);
313          // signing certificate
314        } catch (Exception ex) {
315          throw new CMSException("Error creating attribute: " + ex.toString());   
316        }    
317        // set the attributes
318        signer_info.setSignedAttributes(attributes);
319        // finish the creation of SignerInfo by calling method addSigner
320        try {
321          signed_data.addSignerInfo(signer_info);
322        } catch (NoSuchAlgorithmException ex) {
323          throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
324        }     
325        return signed_data.getEncoded();
326      }
327      
328      
329      /**
330       * Parses a CMS <code>SignedData</code> object and verifies the signatures
331       * for all participated signers.
332       *
333       * @param encoding the DER encoded <code>SignedData</code> object
334       * @param message the the message which was transmitted out-of-band (explicit signed)
335       *
336       * @return the inherent message as byte array
337       * @throws CMSException if any signature does not verify
338       * @throws IOException if some stream I/O error occurs
339       */
340      public byte[] getSignedData(byte[] encoding, byte[] message) throws CMSException, IOException {
341        
342        ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
343        // create the SignedData object
344        SignedData signed_data = new SignedData(encodedStream);
345        
346        if (signed_data.getMode() == SignedData.EXPLICIT) {
347          // in explcit mode explictly supply the content data to do the hash calculation
348          signed_data.setContent(message);
349        }
350        
351        System.out.println("SignedData contains the following signer information:");
352        SignerInfo[] signer_infos = signed_data.getSignerInfos();
353        
354        int numberOfSignerInfos = signer_infos.length;
355        if (numberOfSignerInfos == 0) {
356          String warning = "Warning: Unsigned message (no SignerInfo included)!";  
357          System.err.println(warning);
358          throw new CMSException(warning);
359        } else {
360          for (int i = 0; i < numberOfSignerInfos; i++) {
361            AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm();
362            try {
363              // verify the signed data using the SignerInfo at index i
364              X509Certificate signer_cert = signed_data.verify(i);
365              // if the signature is OK the certificate of the signer is returned
366              System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN());
367              SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
368              if (signingTime != null) {
369                System.out.println("This message has been signed at " + signingTime.get());
370              } 
371              CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
372              if (contentType != null) {
373                System.out.println("The content has CMS content type " + contentType.get().getName());
374              }
375            } catch (SignatureException ex) {
376               // if the signature is not OK a SignatureException is thrown
377               System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
378               throw new CMSException(ex.toString());
379            } 
380          }      
381        
382          // now check alternative signature verification
383          System.out.println("Now check the signature assuming that no certs have been included:");
384          try {
385            SignerInfo signer_info = signed_data.verify(signerCertificates_[0]);
386            AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm();
387            // if the signature is OK the certificate of the signer is returned
388            System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
389              
390          } catch (SignatureException ex) {
391            // if the signature is not OK a SignatureException is thrown
392            System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN());
393            throw new CMSException(ex.toString());
394          }
395          // in practice we also would validate the signer certificate(s)  
396        }
397        
398        return signed_data.getContent();
399      }
400     
401      /**
402       * Tests the CMS SignedData implementation.
403       */
404      public void start() {
405         // the test message
406        String m = "This is the test message.";
407        System.out.println("Test message: \""+m+"\"");
408        System.out.println();
409        byte[] message = m.getBytes();
410       
411        try {
412          byte[] encoding;
413          byte[] received_message = null;
414          System.out.println("Stream implementation demos");
415          System.out.println("===========================");
416          //
417          // test CMS Implicit SignedDataStream
418          //
419          System.out.println("\nImplicit SignedDataStream demo [create]:\n");
420          encoding = createSignedDataStream(message, SignedDataStream.IMPLICIT);
421          // transmit data
422          System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
423          received_message = getSignedDataStream(encoding, null);
424          System.out.print("\nSigned content: ");
425          System.out.println(new String(received_message));
426          
427          //
428          // test CMS Explicit SignedDataStream
429          //
430          System.out.println("\nExplicit SignedDataStream demo [create]:\n");
431          encoding = createSignedDataStream(message, SignedDataStream.EXPLICIT);
432          // transmit data
433          System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
434          received_message = getSignedDataStream(encoding, message);
435          System.out.print("\nSigned content: ");
436          System.out.println(new String(received_message));
437                
438          // the non-stream implementation
439          System.out.println("\nNon-stream implementation demos");
440          System.out.println("===============================");
441       
442          //
443          // test CMS Implicit SignedData
444          //
445          System.out.println("\nImplicit CMS SignedData demo [create]:\n");
446          encoding = createSignedData(message, SignedData.IMPLICIT);
447          // transmit data
448          System.out.println("\nImplicit CMS SignedData demo [parse]:\n");
449          received_message = getSignedData(encoding, null);
450          System.out.print("\nSigned content: ");
451          System.out.println(new String(received_message));
452    
453          //
454          // test CMS Explicit SignedData
455          //
456          System.out.println("\nExplicit CMS SignedData demo [create]:\n");
457          encoding = createSignedData(message, SignedData.EXPLICIT);
458          // transmit data
459          System.out.println("\nExplicit CMS SignedData demo [parse]:\n");
460          received_message = getSignedData(encoding, message);
461          System.out.print("\nSigned content: ");
462          System.out.println(new String(received_message));
463          
464            } catch (Exception ex) {
465              ex.printStackTrace();
466              throw new RuntimeException(ex.toString());
467            }
468      }
469      
470      /**
471       * The main method.
472       * 
473       * @throws IOException 
474       *            if an I/O error occurs when reading required keys
475       *            and certificates from files
476       */
477      public static void main(String argv[]) throws Exception {
478    
479            DemoUtil.initDemos();
480        (new SHA2withDSASignedDataDemo()).start();
481        System.out.println("\nReady!");
482        DemoUtil.waitKey();
483      }
484    }