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