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/CounterSignatureDemo.java 30    12.02.25 17:58 Dbratko $
029// $Revision: 30 $
030//
031
032package demo.cms.signedData;
033
034import iaik.asn1.CodingException;
035import iaik.asn1.ObjectID;
036import iaik.asn1.structures.AlgorithmID;
037import iaik.asn1.structures.Attribute;
038import iaik.asn1.structures.AttributeValue;
039import iaik.cms.CMSException;
040import iaik.cms.ContentInfo;
041import iaik.cms.ContentInfoStream;
042import iaik.cms.IssuerAndSerialNumber;
043import iaik.cms.SignedData;
044import iaik.cms.SignedDataStream;
045import iaik.cms.SignerInfo;
046import iaik.cms.attributes.CMSContentType;
047import iaik.cms.attributes.CounterSignature;
048import iaik.cms.attributes.SigningTime;
049import iaik.utils.Util;
050import iaik.x509.X509Certificate;
051
052import java.io.ByteArrayInputStream;
053import java.io.ByteArrayOutputStream;
054import java.io.IOException;
055import java.io.InputStream;
056import java.security.NoSuchAlgorithmException;
057import java.security.PrivateKey;
058import java.security.SignatureException;
059
060import demo.DemoUtil;
061import demo.keystore.CMSKeyStore;
062
063/**
064 * This class demonstrates the usage of the CounterSignature attribute.
065 * <p>
066 * A {@link iaik.cms.attributes.CounterSignature CounterSignature} attribute may be included
067 * as an unsigned attribute into a {@link iaik.cms.SignerInfo SignerInfo} for counter signing
068 * (signing in serial) the signature value of a SignerInfo included in a SignedData. The value
069 * of a CounterSignature attribute itself is a SignerInfo.
070 * <p>
071 * This demo shows how a CounterSignature attribute may be added to some SignerInfo that belongs
072 * to a SignedData object just parsed/verified. This class demonstrates adding/verifying of a
073 * CounterSignature attribute to both the {@link iaik.cms.SignedDataStream stream} and the
074 * {@link iaik.cms.SignedData non-stream} implementations of the SignedData type. Since when
075 * parsing an implicit -- where the content is included -- SignedData object, SignerInfos
076 * can not accessed before the data has been processed, adding a counter signature to 
077 * a {@link iaik.cms.SignedDataStream SignedDataStream} may require a different proceeding
078 * than adding it to a {@link iaik.cms.SignedData SignedData} object. For that reason a
079 * {@link CounterSignatureListener CounterSignatureListener} is used for the
080 * stream demos to listen on and add the counter signature during the encoding process.
081 *
082 * @see CounterSignatureListener
083 * @see iaik.cms.attributes.CounterSignature
084 * @see iaik.cms.SDSEncodeListener
085 * @see iaik.cms.SignedDataStream
086 * @see iaik.cms.SignerInfo
087 */
088public class CounterSignatureDemo {
089
090  byte[] message;
091  
092  // signing certificate of user 1
093  X509Certificate user1_sign;
094  // signing private key of user 1
095  PrivateKey user1_sign_pk;
096  // signing certificate of user 2 (counter signer)
097  X509Certificate user2_sign;
098  // signing private key of user 2 (counter signer)
099  PrivateKey user2_sign_pk;
100  
101  // a certificate chain containing the user certs + CA
102  X509Certificate[] certificates;
103
104  /**
105   * Constructor.
106   * Reads required keys/certs from the demo keystore.
107   */
108  public CounterSignatureDemo() {
109    
110    System.out.println();
111    System.out.println("**********************************************************************************");
112    System.out.println("*                      CounterSignatureDemo demo                                 *");
113    System.out.println("*       (shows the usage of the CounterSignature attribute implementation)       *");
114    System.out.println("**********************************************************************************");
115    System.out.println();
116    
117    message = "This is a test of the CMS implementation!".getBytes();
118    // signing certs
119    certificates = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
120    user1_sign = certificates[0];
121    user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
122    user2_sign = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1)[0];
123    user2_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
124  }
125  
126  /**
127   * Creates a CMS <code>SignedData</code> object.
128   * <p>
129   *
130   * @param message the message to be signed, as byte representation
131   * @param mode the mode indicating whether to include the content 
132   *        (SignedDataStream.IMPLICIT) or not (SignedDataStream.EXPLICIT)
133   * @return the encoding of the <code>SignedData</code> object just created
134   * @throws Exception if the <code>SignedData</code> object cannot
135   *                      be created for some reason
136   */
137  public byte[] createSignedDataStream(byte[] message, int mode) throws Exception {
138
139    System.out.println("Create a new message signed by user 1:");
140
141    // we are testing the stream interface
142    ByteArrayInputStream is = new ByteArrayInputStream(message);
143    // create a new SignedData object which includes the data
144    SignedDataStream signed_data = new SignedDataStream(is, mode);
145    
146    // SignedData shall include the certificate chain for verifying
147    signed_data.setCertificates(certificates);
148
149    // cert at index 0 is the user certificate
150    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
151
152    // create a new SignerInfo
153    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
154    // create some authenticated attributes
155    // the message digest attribute is automatically added
156    Attribute[] attributes = new Attribute[2];
157    // content type is data
158    attributes[0] = new Attribute(new CMSContentType(ObjectID.cms_data));
159    // signing time is now
160    attributes[1] = new Attribute(new SigningTime());
161    // set the attributes
162    signer_info.setSignedAttributes(attributes);
163    // finish the creation of SignerInfo by calling method addSigner
164    try {
165      signed_data.addSignerInfo(signer_info);
166
167    } catch (NoSuchAlgorithmException ex) {
168      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
169    }
170    // ensure block encoding
171    signed_data.setBlockSize(2048);
172
173    // write the data through SignedData to any out-of-band place
174    if (mode == SignedDataStream.EXPLICIT) {
175      InputStream data_is = signed_data.getInputStream();
176      byte[] buf = new byte[1024];
177      int r;
178      while ((r = data_is.read(buf)) > 0) {
179        ;   // skip data
180      }  
181    }
182
183    // return the SignedData as encoded byte array with block size 2048
184    ByteArrayOutputStream os = new ByteArrayOutputStream();
185    // wrap into ContentInfo
186    ContentInfoStream ci = new ContentInfoStream(signed_data);
187    ci.writeTo(os);
188    return os.toByteArray();
189  }
190
191  /**
192   * Parses a CMS <code>SignedData</code> object and verifies the signatures
193   * for all participated signers. 
194   *
195   * @param signedData the SignedData, as BER encoded byte array
196   * @param message the the message which was transmitted out-of-band (explicit signed)
197   * @param counterSign whether to use a SDSEncodeListener to add a SignerInfo
198   *        and encode the SignedData again
199   *
200   * @return the inherent message as byte array, or the BER encoded SignedData if
201   *         it shall be encoded again (counter signing phase)
202   * @throws Exception if an error occurs
203   */
204  public byte[] getSignedDataStream(byte[] signedData, byte[] message, boolean counterSign) 
205    throws Exception {
206
207    // we are testing the stream interface
208    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
209    
210    // the ByteArrayOutputStream to which to write the content
211    ByteArrayOutputStream os = new ByteArrayOutputStream();
212        
213    SignedDataStream signed_data = new SignedDataStream(is);
214    
215    // content included (implicit mode)?
216    boolean implicit = (signed_data.getMode() == SignedDataStream.IMPLICIT); 
217    
218    if (implicit == false) {
219      // in explicit mode explicitly supply the content for hash computation  
220      signed_data.setInputStream(new ByteArrayInputStream(message));
221    }
222
223    
224    if (counterSign) {
225      // we want to write the SignedData again   
226      // we add a counter signature attribute to the first signer
227            
228      // add the CounterSignature via SDSEncodeListener
229      CounterSignatureListener csl = 
230        new CounterSignatureListener(new IssuerAndSerialNumber(user2_sign), 
231                                     (AlgorithmID)AlgorithmID.sha256.clone(),
232                                     user2_sign_pk);
233      // we only want to counter sign some specific signer  
234      csl.setCertOfSignerToBeCounterSigned(user1_sign);
235      
236      if (implicit) {
237        // in implicit mode copy data to os
238        csl.setOutputStream(os);
239        signed_data.setSDSEncodeListener(csl);     
240
241      } else {
242        signed_data.setSDSEncodeListener(csl);
243        // get an InputStream for reading the signed content
244        InputStream data = signed_data.getInputStream();
245        Util.copyStream(data, os, null);
246      } 
247       
248      // ensure block encoding
249      signed_data.setBlockSize(2048);
250      // return the SignedData as encoded byte array with block size 2048
251      ByteArrayOutputStream baos = new ByteArrayOutputStream();
252      // wrap into ContentInfo
253      ContentInfoStream ci = new ContentInfoStream(signed_data);
254      ci.writeTo(baos);
255      
256      // we read the content
257      byte[] content = os.toByteArray();
258      System.out.println("Content: " + new String(content));
259      
260      // return encoded SignedData
261      return baos.toByteArray();
262
263    } else {  
264
265      // get an InputStream for reading the signed content
266      InputStream data = signed_data.getInputStream();
267      os = new ByteArrayOutputStream();
268      Util.copyStream(data, os, null);
269
270      System.out.println("SignedData contains the following signer information:");
271      SignerInfo[] signer_infos = signed_data.getSignerInfos();
272
273      int numberOfSignerInfos = signer_infos.length;
274      if (numberOfSignerInfos == 0) {
275        String warning = "Warning: Unsigned message (no SignerInfo included)!";  
276        System.err.println(warning);
277        throw new CMSException(warning);
278      } else {
279        for (int i = 0; i < numberOfSignerInfos; i++) {
280          try {
281            // verify the signed data using the SignerInfo at index i
282            X509Certificate signer_cert = signed_data.verify(i);
283            // if the signature is OK the certificate of the signer is returned
284            System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
285            // get signed attributes
286            // signing time
287            SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
288            if (signingTime != null) {
289              System.out.println("This message has been signed at " + signingTime.get());
290            } 
291            // content type
292            CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
293            if (contentType != null) {
294              System.out.println("The content has CMS content type " + contentType.get().getName());
295            }
296            // counter signature?
297            Attribute counterSignatureAttribute = signer_infos[i].getUnsignedAttribute(ObjectID.countersignature);
298            if (counterSignatureAttribute != null) {
299              AttributeValue[] counterSignatures = counterSignatureAttribute.getAttributeValues();
300              System.out.println("This SignerInfo is counter signed from: ");
301              for (int j = 0; j < counterSignatures.length; j++) {
302                CounterSignature counterSignature = (CounterSignature)counterSignatures[j];
303                try {
304                  if (counterSignature.verify(user2_sign.getPublicKey(), signer_infos[i])) {
305                    System.out.println("Signature OK from counter signer: "+counterSignature.getSignerIdentifier());  
306                  } else {
307                    System.out.println("Signature ERROR from counter signer: "+counterSignature.getSignerIdentifier());  
308                  }  
309                } catch (SignatureException ex) {
310                  System.out.println("Signature ERROR from counter signer: "+counterSignature.getSignerIdentifier());  
311                  throw new CMSException(ex.toString());
312                }  
313                signingTime = (SigningTime)counterSignature.getSignedAttributeValue(ObjectID.signingTime);
314                if (signingTime != null) {
315                  System.out.println("Counter signature has been created " + signingTime.get());
316                } 
317              }  
318            }
319            
320          } catch (SignatureException ex) {
321            // if the signature is not OK a SignatureException is thrown
322            System.err.println("Signature ERROR from signer: "+signed_data.getCertificate((signer_infos[i].getSignerIdentifier())).getSubjectDN());
323            throw new CMSException(ex.toString());
324          } catch (CodingException ex) {
325            throw new CMSException("Attribute decoding error: " + ex.toString());
326          }
327        }
328        // in practice we also would validate the signer certificate(s)  
329      }
330      
331      // return content
332      return os.toByteArray();
333    }
334  }
335  
336  
337  /**
338   * Creates a CMS <code>SignedData</code> object.
339   * <p>
340   *
341   * @param message the message to be signed, as byte representation
342   * @param mode the mode indicating whether to include the content 
343   *        (SignedDataStream.IMPLICIT) or not (SignedDataStream.EXPLICIT)
344   * @return the encoding of the <code>SignedData</code> object just created
345   * @throws CMSException if the <code>SignedData</code> object cannot
346   *                          be created
347   * @throws Exception if an error occurs
348   */
349  public byte[] createSignedData(byte[] message, int mode) throws Exception  {
350
351    System.out.println("Create a new message signed by user 1:");
352
353    // create a new SignedData object
354    SignedData signed_data = new SignedData(message, mode);
355    
356    // SignedData shall include the certificate chain for verifying
357    signed_data.setCertificates(certificates);
358
359    // cert at index 0 is the user certificate
360    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
361
362    // create a new SignerInfo
363    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
364    // create some authenticated attributes
365    // the message digest attribute is automatically added
366    Attribute[] attributes = new Attribute[2];
367    // content type is data
368    attributes[0] = new Attribute(new CMSContentType(ObjectID.cms_data));
369    // signing time is now
370    attributes[1] = new Attribute(new SigningTime());
371    // set the attributes
372    signer_info.setSignedAttributes(attributes);
373    // finish the creation of SignerInfo by calling method addSigner
374    try {
375      signed_data.addSignerInfo(signer_info);
376
377    } catch (NoSuchAlgorithmException ex) {
378      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
379    }
380    
381    // return the SignedData as encoded byte array with block size 2048
382    ByteArrayOutputStream os = new ByteArrayOutputStream();
383    // wrap into ContentInfo
384    ContentInfo ci = new ContentInfo(signed_data);
385    ci.writeTo(os);
386    return os.toByteArray();
387  }
388
389  /**
390   * Parses a CMS <code>SignedData</code> object and verifies the signatures
391   * for all participated signers. 
392   *
393   * @param signedData the SignedData, as BER encoded byte array
394   * @param message the the message which was transmitted out-of-band (explicit signed)
395   * @param counterSign whether to use a SDSEncodeListener to add a SignerInfo
396   *        and encode the SignedData again
397   *
398   * @return the inherent message as byte array, or the BER encoded SignedData if
399   *         it shall be encoded again (counter signing phase)
400   * @throws Exception if any error occurs
401   */
402  public byte[] getSignedData(byte[] signedData, byte[] message, boolean counterSign) 
403    throws Exception {
404
405    // we are testing the stream interface
406    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
407        
408    SignedData signed_data = new SignedData(is);
409    
410    // content included (implicit mode)?
411    boolean implicit = (signed_data.getMode() == SignedData.IMPLICIT);
412    if (implicit == false) {
413      // in explcit mode explictly supply the content data to do the hash calculation
414      signed_data.setContent(message);
415    }
416
417    System.out.println("SignedData contains the following signer information:");
418    SignerInfo[] signer_infos = signed_data.getSignerInfos();
419
420    int numberOfSignerInfos = signer_infos.length;
421    if (numberOfSignerInfos == 0) {
422      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
423      System.err.println(warning);
424      throw new CMSException(warning);
425    } else {
426      for (int i = 0; i < numberOfSignerInfos; i++) {
427        try {
428          // verify the signed data using the SignerInfo at index i
429          X509Certificate signer_cert = signed_data.verify(i);
430          // if the signature is OK the certificate of the signer is returned
431          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
432          // get signed attributes
433          // signing time
434          SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
435          if (signingTime != null) {
436            System.out.println("This message has been signed at " + signingTime.get());
437          } 
438          // content type
439          CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
440          if (contentType != null) {
441            System.out.println("The content has CMS content type " + contentType.get().getName());
442          }
443          // counter signature?
444          Attribute counterSignatureAttribute = signer_infos[i].getUnsignedAttribute(ObjectID.countersignature);
445          if (counterSignatureAttribute != null) {
446            AttributeValue[] counterSignatures = counterSignatureAttribute.getAttributeValues();
447            System.out.println("This SignerInfo is counter signed from: ");
448            for (int j = 0; j < counterSignatures.length; j++) {
449              CounterSignature counterSignature = (CounterSignature)counterSignatures[j];
450              try {
451                if (counterSignature.verify(user2_sign.getPublicKey(), signer_infos[i])) {
452                  System.out.println("Signature OK from counter signer: "+counterSignature.getSignerIdentifier());  
453                } else {
454                  System.out.println("Signature ERROR from counter signer: "+counterSignature.getSignerIdentifier());  
455                }  
456              } catch (SignatureException ex) {
457                System.out.println("Signature ERROR from counter signer: "+counterSignature.getSignerIdentifier());  
458                throw new CMSException(ex.toString());
459              }  
460              signingTime = (SigningTime)counterSignature.getSignedAttributeValue(ObjectID.signingTime);
461              if (signingTime != null) {
462                System.out.println("Counter signature has been created " + signingTime.get());
463              } 
464            }  
465          }    
466  
467        } catch (SignatureException ex) {
468          // if the signature is not OK a SignatureException is thrown
469          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate((signer_infos[i].getSignerIdentifier())).getSubjectDN());
470          throw new CMSException(ex.toString());
471        } catch (CodingException ex) {
472          throw new CMSException("Attribute decoding error: " + ex.toString());
473        }
474      }
475      // in practice we also would validate the signer certificate(s)  
476    }
477    
478    if (counterSign) {
479      // we want to write the SignedData again   
480      // we add a counter signature attribute to the first signer
481      CounterSignature counterSignature = new CounterSignature(new IssuerAndSerialNumber(user2_sign),
482          (AlgorithmID)AlgorithmID.sha256.clone(), user2_sign_pk);
483      // create some authenticated attributes
484      // the message digest attribute is automatically added
485      // signing time is now
486      SigningTime signingTime = new SigningTime();
487      Attribute[] attributes = { new Attribute(signingTime) };
488      // set the attributes
489      counterSignature.setSignedAttributes(attributes);
490      // now counter sign first SignerInfo
491      counterSignature.counterSign(signer_infos[0]);
492      // and add the counter signature as unsigned attribute
493      Attribute[] usignedAttributes = { new Attribute(counterSignature) };
494      signer_infos[0].addUnsignedAttributes(usignedAttributes);
495      
496      ByteArrayOutputStream baos = new ByteArrayOutputStream();
497      ContentInfo ci = new ContentInfo(signed_data);
498      ci.writeTo(baos);
499      signed_data.writeTo(baos);
500      
501      // we read the content
502      System.out.println("Content: " + new String(signed_data.getContent()));
503      
504      return baos.toByteArray();
505
506    } else {
507      // return the content     
508      return signed_data.getContent();
509    }  
510    
511    
512  }
513
514  /**
515   * Starts the demo.
516   */
517  public void start() {
518
519    try {
520        
521      byte[] data;
522      byte[] received_message = null;  
523      
524      //
525      // test CMS Implicit SignedDataStream
526      //
527      System.out.println("\nImplicit SignedDataStream demo [create]:\n");
528      data = createSignedDataStream(message, SignedDataStream.IMPLICIT);
529      // parse and encode again
530      System.out.println("\nImplicit SignedDataStream demo [counter sign]:\n");
531      data = getSignedDataStream(data, null, true);
532      // parse
533      System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
534      received_message = getSignedDataStream(data, null, false);
535      System.out.print("\nSigned content: ");
536      System.out.println(new String(received_message));
537
538      //
539      // test CMS Explicit SignedDataStream
540      //
541      System.out.println("\nExplicit SignedDataStream demo [create]:\n");
542      data = createSignedDataStream(message, SignedDataStream.EXPLICIT);
543      // parse and encode again
544      System.out.println("\nExplicit SignedDataStream demo [counter sign]:\n");
545      data = getSignedDataStream(data, message, true);
546      
547      System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
548      received_message = getSignedDataStream(data, message, false);
549      System.out.print("\nSigned content: ");
550      System.out.println(new String(received_message));
551      
552      //
553      // test CMS Implicit SignedData
554      //
555      System.out.println("\nImplicit SignedData demo [create]:\n");
556      data = createSignedData(message, SignedData.IMPLICIT);
557      // parse and encode again
558      System.out.println("\nImplicit SignedData demo [counter sign]:\n");
559      data = getSignedData(data, null, true);
560      // parse
561      System.out.println("\nImplicit SignedData demo [parse]:\n");
562      received_message = getSignedData(data, null, false);
563      System.out.print("\nSigned content: ");
564      System.out.println(new String(received_message));
565
566      //
567      // test CMS Explicit SignedData
568      //
569      System.out.println("\nExplicit SignedData demo [create]:\n");
570      data = createSignedData(message, SignedData.EXPLICIT);
571      // parse and encode again
572      System.out.println("\nExplicit SignedData demo [counter sign]:\n");
573      data = getSignedData(data, message, true);
574      
575      System.out.println("\nExplicit SignedData demo [parse]:\n");
576      received_message = getSignedData(data, message, false);
577      System.out.print("\nSigned content: ");
578      System.out.println(new String(received_message));
579
580        } catch (Exception ex) {
581          ex.printStackTrace();
582          throw new RuntimeException(ex.toString());
583        }
584        }
585
586    
587  /**
588   * Main method.
589   * 
590   * @throws IOException 
591   *            if an I/O error occurs when reading required keys
592   *            and certificates from files
593   */
594  public static void main(String argv[]) throws IOException {
595   try {
596     DemoUtil.initDemos();
597     (new CounterSignatureDemo()).start();
598     System.out.println("\nReady!");
599   } catch (Exception ex) {    
600     ex.printStackTrace();      
601   }
602   DemoUtil.waitKey();
603  }
604}