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/smime/ess/SigningCertificateDemo.java 17    12.02.25 17:59 Dbratko $
029// $Revision: 17 $
030//
031
032package demo.smime.ess;
033
034import iaik.asn1.ASN1Object;
035import iaik.asn1.ObjectID;
036import iaik.asn1.SEQUENCE;
037import iaik.asn1.UTF8String;
038import iaik.asn1.structures.AlgorithmID;
039import iaik.asn1.structures.Attribute;
040import iaik.asn1.structures.GeneralName;
041import iaik.asn1.structures.GeneralNames;
042import iaik.asn1.structures.Name;
043import iaik.asn1.structures.PolicyInformation;
044import iaik.asn1.structures.PolicyQualifierInfo;
045import iaik.cms.CMSException;
046import iaik.cms.IssuerAndSerialNumber;
047import iaik.cms.SignedData;
048import iaik.cms.SignedDataStream;
049import iaik.cms.SignerInfo;
050import iaik.cms.attributes.CMSContentType;
051import iaik.cms.attributes.SigningTime;
052import iaik.smime.ess.SigningCertificate;
053import iaik.utils.Util;
054import iaik.x509.X509Certificate;
055import iaik.x509.attr.AttributeCertificate;
056import iaik.x509.attr.Holder;
057import iaik.x509.attr.V1Form;
058
059import java.io.ByteArrayInputStream;
060import java.io.ByteArrayOutputStream;
061import java.io.IOException;
062import java.io.InputStream;
063import java.math.BigInteger;
064import java.security.NoSuchAlgorithmException;
065import java.security.PrivateKey;
066import java.security.SignatureException;
067import java.security.cert.Certificate;
068import java.util.Calendar;
069import java.util.Date;
070import java.util.GregorianCalendar;
071
072import demo.DemoUtil;
073import demo.keystore.CMSKeyStore;
074
075
076/**
077 * Demonstrates how to add and parse a {@link iaik.smime.ess.SigningCertificate 
078 * SigningCertificate} attribute to the SignerInfo of a {@link iaik.cms.SignedDataStream} or
079 * {@link iaik.cms.SignedData} object. The SigningCertificate attributes maybe used
080 * to include certificate identification information into the signed attributes of a 
081 * CMS {@link iaik.cms.SignerInfo SignerInfo} object.
082 *
083 * @see iaik.smime.ess.SigningCertificate
084 * @see iaik.cms.SignerInfo
085 * @see iaik.cms.SignedDataStream
086 * @see iaik.cms.SignedData
087 */
088public class SigningCertificateDemo {
089
090  // certificate of user 1
091  X509Certificate user1;
092  // private key of user 1
093  PrivateKey user1_pk;
094
095  // a certificate chain containing the user certs + CA
096  Certificate[] certs;
097  Certificate[] user1Certs;
098  
099  // just for attribute certificate testing
100  PrivateKey issuer1_pk;
101
102  /**
103   * Setups the demo certificate chains.
104   * 
105   * Keys and certificate are retrieved from the demo KeyStore.
106   * 
107   * @throws IOException if an file read error occurs
108   */
109  public SigningCertificateDemo() throws IOException {
110    
111    System.out.println();
112    System.out.println("**********************************************************************************");
113    System.out.println("*                       SigningCertificateDemo demo                              *");
114    System.out.println("*          (shows the usage of the ESS SigningCertificate attribute)             *");
115    System.out.println("**********************************************************************************");
116    System.out.println();
117    
118    // add all certificates to the list
119    user1Certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
120    user1 = (X509Certificate)user1Certs[0];
121    user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
122    
123    certs = user1Certs;
124    try {
125      issuer1_pk = CMSKeyStore.getCaPrivateKey(CMSKeyStore.RSA);
126      AttributeCertificate attrCert = createAttributeCertificate();
127      certs = new Certificate[user1Certs.length+1];  
128      System.arraycopy(user1Certs, 0, certs, 0, user1Certs.length);
129      certs[user1Certs.length] = attrCert;
130    } catch (CMSException ex) {
131      System.out.println("No attribute certificates!");   
132    }    
133  }
134  
135  /**
136   * Creates a CMS <code>SignedData</code> object.
137   * <p>
138   *
139   * @param message the message to be signed, as byte representation
140   * @param mode the transmission mode, either IMPLICIT or EXPLICIT
141   * @return the BER encoding of the <code>SignedData</code> object just created
142   * @throws CMSException if the <code>SignedData</code> object cannot
143   *                          be created
144   * @throws IOException if some stream I/O error occurs
145   */
146  public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException  {
147    
148    System.out.print("Create a new message signed by user 1 :");
149   
150    // we are testing the stream interface
151    ByteArrayInputStream is = new ByteArrayInputStream(message);
152    // create a new SignedData object which includes the data
153    SignedDataStream signed_data = new SignedDataStream(is, mode);
154    
155    // SignedData shall include the certificate chain for verifying
156    signed_data.setCertificates(certs);
157    
158    // cert at index 0 is the user certificate
159    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1);
160
161    // create a new SignerInfo
162    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha1.clone(), user1_pk);
163    // create some authenticated attributes
164    // the message digest attribute is automatically added
165    Attribute[] attributes = new Attribute[3];
166    try {
167      // content type is data
168      CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
169      attributes[0] = new Attribute(contentType);
170      // signing time is now
171      SigningTime signingTime = new SigningTime();
172      attributes[1] = new Attribute(signingTime);
173      // signing certificate
174      SigningCertificate signingCertificate = createSigningCertificate(certs);
175      String explicitText = "This certificate only may be used for test purposes";
176      PolicyQualifierInfo policyQualifier = new PolicyQualifierInfo(null, null, explicitText);
177      PolicyInformation[] policyInformations = 
178        { new PolicyInformation(new ObjectID("1.3.6.1.4.1.2706.2.2.4.1.1.1.1"),
179                              new PolicyQualifierInfo[] { policyQualifier }) };
180      signingCertificate.setPolicies(policyInformations);                        
181      System.out.println("Include signingCertificate attribute:");
182      System.out.println(signingCertificate);
183      attributes[2] = new Attribute(signingCertificate);
184    } catch (Exception ex) {
185      throw new CMSException("Error creating attribute: " + ex.toString());   
186    }    
187    // set the attributes
188    signer_info.setSignedAttributes(attributes);
189    // finish the creation of SignerInfo by calling method addSigner
190    try {
191      signed_data.addSignerInfo(signer_info);
192    } catch (NoSuchAlgorithmException ex) {
193      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
194    } 
195    // write the data through SignedData to any out-of-band place
196    if (mode == SignedDataStream.EXPLICIT) {
197      InputStream data_is = signed_data.getInputStream();
198      byte[] buf = new byte[1024];
199      int r;
200      while ((r = data_is.read(buf)) > 0)
201        ;   // skip data
202    }
203
204    // return the SignedData as DER encoded byte array with block size 2048
205    ByteArrayOutputStream os = new ByteArrayOutputStream();
206    signed_data.writeTo(os, 2048);
207    return os.toByteArray();
208  }
209  
210
211  /**
212   * Parses a CMS <code>SignedData</code> object and verifies the signatures
213   * for all participated signers.
214   *
215   * @param signedData <code>SignedData</code> object as BER encoded byte array
216   * @param message the the message which was transmitted out-of-band (explicit signed)
217   *
218   * @return the inherent message as byte array
219   * @throws CMSException if any signature does not verify
220   * @throws IOException if some stream I/O error occurs
221   */
222  public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException {
223
224    // we are testing the stream interface
225    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
226    // create the SignedData object
227    SignedDataStream signed_data = new SignedDataStream(is);
228    
229    if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
230      // in explicit mode explicitly supply the content for hash computation  
231      signed_data.setInputStream(new ByteArrayInputStream(message));
232    }
233
234    // get an InputStream for reading the signed content
235    InputStream data = signed_data.getInputStream();
236    ByteArrayOutputStream os = new ByteArrayOutputStream();
237    Util.copyStream(data, os, null);
238    
239    System.out.println("SignedData contains the following signer information:");
240    SignerInfo[] signer_infos = signed_data.getSignerInfos();
241    
242    int numberOfSignerInfos = signer_infos.length;
243    if (numberOfSignerInfos == 0) {
244      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
245      System.err.println(warning);
246      throw new CMSException(warning);
247    } else {
248      for (int i = 0; i < numberOfSignerInfos; i++) {
249        
250        try {
251          // verify the signed data using the SignerInfo at index i
252          X509Certificate signer_cert = signed_data.verify(i);
253          // if the signature is OK the certificate of the signer is returned
254          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
255          SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
256          if (signingTime != null) {
257            System.out.println("This message has been signed at " + signingTime.get());
258          } 
259          CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
260          if (contentType != null) {
261            System.out.println("The content has CMS content type " + contentType.get().getName());
262          }
263          // check SigningCertificate attribute
264          try {
265            SigningCertificate signingCertificate = getSigningCertificate(signer_infos[i]);
266            if (signingCertificate != null) {
267              checkSigningCertificate(signingCertificate, signer_cert, signed_data, i);  
268            }  
269          } catch (CMSException ex) {
270            throw new CMSException("Error parsing SigningCertificate attribute: " + ex.getMessage());   
271          }    
272         
273        } catch (SignatureException ex) {
274          // if the signature is not OK a SignatureException is thrown
275          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
276          throw new CMSException(ex.toString());
277        }  
278      }  
279    
280      // now check alternative signature verification
281      System.out.println("Now check the signature assuming that no certs have been included:");
282      try {
283         SignerInfo signer_info = signed_data.verify(user1);
284          // if the signature is OK the certificate of the signer is returned
285          System.out.println("Signature OK from signer: "+user1.getSubjectDN());
286          
287      } catch (SignatureException ex) {
288          // if the signature is not OK a SignatureException is thrown
289          System.out.println("Signature ERROR from signer: "+user1.getSubjectDN());
290          throw new CMSException(ex.toString());
291      }
292      // in practice we also would validate the signer certificate(s)  
293    }      
294  
295    System.out.println("Included attribute certificates:");
296    AttributeCertificate[] attributeCerts = signed_data.getAttributeCertificates();
297    if (attributeCerts == null) {
298      System.out.println("No attribute certificates");   
299    } else {   
300      for (int i = 0; i < attributeCerts.length; i++) {
301        System.out.println(attributeCerts[i].getHolder());   
302      } 
303    } 
304       
305    return os.toByteArray();
306  }
307  
308  
309  
310  /**
311   * Creates a CMS <code>SignedData</code> object.
312   * <p>
313   *
314   * @param message the message to be signed, as byte representation
315   * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
316   * @return the DER encoded <code>SignedData</code> object
317   * @throws CMSException if the <code>SignedData</code> object cannot
318   *                          be created
319   */
320  public byte[] createSignedData(byte[] message, int mode) throws CMSException {
321    
322    System.out.println("Create a new message signed by user 1 :");
323  
324    // create a new SignedData object which includes the data
325    SignedData signed_data = new SignedData(message, mode);
326    
327    // SignedData shall include the certificate chain for verifying
328    signed_data.setCertificates(certs);
329  
330    // cert at index 0 is the user certificate
331    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1);
332
333    // create a new SignerInfo
334    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha1.clone(), user1_pk);
335    // create some authenticated attributes
336    // the message digest attribute is automatically added
337    Attribute[] attributes = new Attribute[3];
338    try {
339      // content type is data
340      CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
341      attributes[0] = new Attribute(contentType);
342      // signing time is now
343      SigningTime signingTime = new SigningTime();
344      attributes[1] = new Attribute(signingTime);
345      // signing certificate
346      SigningCertificate signingCertificate = createSigningCertificate(certs);
347      System.out.println("Include signingCertificate attribute:");
348      System.out.println(signingCertificate);
349      attributes[2] = new Attribute(signingCertificate);
350    } catch (Exception ex) {
351      throw new CMSException("Error creating attribute: " + ex.toString());   
352    }    
353    // set the attributes
354    signer_info.setSignedAttributes(attributes);
355    // finish the creation of SignerInfo by calling method addSigner
356    try {
357      signed_data.addSignerInfo(signer_info);
358    } catch (NoSuchAlgorithmException ex) {
359      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
360    }     
361    return signed_data.getEncoded();
362  }
363  
364  
365  /**
366   * Parses a CMS <code>SignedData</code> object and verifies the signatures
367   * for all participated signers.
368   *
369   * @param encoding the DER encoded <code>SignedData</code> object
370   * @param message the the message which was transmitted out-of-band (explicit signed)
371   *
372   * @return the inherent message as byte array
373   * @throws CMSException if any signature does not verify
374   * @throws IOException if some stream I/O error occurs
375   */
376  public byte[] getSignedData(byte[] encoding, byte[] message) throws CMSException, IOException {
377    
378    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
379    // create the SignedData object
380    SignedData signed_data = new SignedData(encodedStream);
381    
382    if (signed_data.getMode() == SignedData.EXPLICIT) {
383      // in explcit mode explictly supply the content data to do the hash calculation
384      signed_data.setContent(message);
385    }
386    
387    System.out.println("SignedData contains the following signer information:");
388    SignerInfo[] signer_infos = signed_data.getSignerInfos();
389    
390    int numberOfSignerInfos = signer_infos.length;
391    if (numberOfSignerInfos == 0) {
392      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
393      System.err.println(warning);
394      throw new CMSException(warning);
395    } else {
396      for (int i = 0; i < numberOfSignerInfos; i++) {
397        try {
398          // verify the signed data using the SignerInfo at index i
399          X509Certificate signer_cert = signed_data.verify(i);
400          // if the signature is OK the certificate of the signer is returned
401          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
402          SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
403          if (signingTime != null) {
404            System.out.println("This message has been signed at " + signingTime.get());
405          } 
406          CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
407          if (contentType != null) {
408            System.out.println("The content has CMS content type " + contentType.get().getName());
409          }
410          // check SigningCertificate attribute
411          SigningCertificate signingCertificate = getSigningCertificate(signer_infos[i]);
412          if (signingCertificate != null) {
413            checkSigningCertificate(signingCertificate, signer_cert, signed_data, i);
414          }
415        } catch (SignatureException ex) {
416           // if the signature is not OK a SignatureException is thrown
417           System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
418           throw new CMSException(ex.toString());
419        } 
420      }      
421    
422      // now check alternative signature verification
423      System.out.println("Now check the signature assuming that no certs have been included:");
424      try {
425        SignerInfo signer_info = signed_data.verify(user1);
426        // if the signature is OK the certificate of the signer is returned
427        System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
428          
429      } catch (SignatureException ex) {
430        // if the signature is not OK a SignatureException is thrown
431        System.out.println("Signature ERROR from signer: "+user1.getSubjectDN());
432        throw new CMSException(ex.toString());
433      }
434      // in practice we also would validate the signer certificate(s)  
435    }  
436    return signed_data.getContent();
437  }
438  
439  /**
440   * Checks the SigningCertificate attribute.
441   *
442   * @param signingCertificate the SigningCertificate attribute
443   * @param signerCert the certificate of the signer
444   * @param signedData the SignedData containing the SignerInfo with the SigningCertificate
445   *                   attribute to be checked
446   * @param signerInfoIndex the index of the SignerInfo with the SigningCertificate
447   *                   attribute to be checked
448   *
449   * @throws CMSException if the SigningCertificate check fails
450   */
451  private void checkSigningCertificate(SigningCertificate signingCertificate,
452                                       X509Certificate signerCert,
453                                       SignedDataStream signedData,
454                                       int signerInfoIndex) throws CMSException {
455    try {
456      if (signingCertificate != null) {
457        System.out.println("SigningCertificate attribute included!");
458        if (!signingCertificate.isSignerCertificate(signerCert)) {
459          throw new CMSException("Cert ERROR!!! The certificate used for signing is not the one " +
460                                 "identified by the SignerCertificate attribute!");
461        } else {
462          System.out.println("SigningCertificate attribute: Signer cert ok!");   
463        } 
464        // get the authorization certs for this signerInfo
465        Certificate[] authCerts = 
466          signingCertificate.getAuthorizedCertificates(signedData.getCertificates());
467        if (authCerts != null) {
468          System.out.println("SignedData contains the following authorization certs for SignerInfo No " + (signerInfoIndex+1) +":");   
469          for (int j = 0; j < authCerts.length; j++) {
470            if (authCerts[j].getType().equalsIgnoreCase("X.509")) {
471              System.out.println("X.509 public key cert: " + ((X509Certificate)authCerts[j]).getSubjectDN());
472            } else {
473              System.out.println("X.509 attribute cert: " + ((AttributeCertificate)authCerts[j]).getHolder());  
474            }     
475          }  
476        } 
477        if (signingCertificate.countPolicies() > 0) {
478          // get the certs with PolicyInformations according to the SigningCertificate attribute:
479          Certificate[] policyCerts = 
480            signingCertificate.getPolicyInformationCerts(signedData.getCertificates());
481          if (policyCerts != null) {
482            System.out.println("SignedData contains the following certs corresponding to policy informations of SignerInfo No "
483                               + (signerInfoIndex+1) +":");   
484            for (int j = 0; j < policyCerts.length; j++) {
485              if (policyCerts[j].getType().equalsIgnoreCase("X.509")) {
486                System.out.println("X.509 public key cert: " + ((X509Certificate)policyCerts[j]).getSubjectDN());
487              } else {
488                System.out.println("X.509 attribute cert: " + ((AttributeCertificate)policyCerts[j]).getHolder());  
489              }     
490            }  
491          }
492        }  
493      }  
494    } catch (NoSuchAlgorithmException ex) {
495      throw new CMSException("Cannot check SigningCertificate attribute: Algorithm SHA not implemented!");
496    } 
497  }      
498  
499  /**
500   * Creates an attribute certificate just for testing.
501   *
502   * @return the attribute certificate created
503   * @throws CMSException if an error occurs when creating the attribute certificate
504   */
505  public AttributeCertificate createAttributeCertificate() throws CMSException {
506    try {
507     
508      Name issuer = (Name)user1.getIssuerDN();
509      GeneralName genName = new GeneralName(GeneralName.directoryName, issuer);
510      GeneralNames genNames = new GeneralNames(genName);
511      V1Form v1Form = new V1Form(genNames);
512      Name subject = (Name)user1.getSubjectDN();
513      GeneralName genName1 = new GeneralName(GeneralName.directoryName, subject);
514      GeneralNames genNames1 = new GeneralNames(genName1);
515      Holder holder = new Holder();
516      holder.setEntityName(genNames1);
517
518      AttributeCertificate cert = new AttributeCertificate();
519      cert.setHolder(holder);
520      cert.setIssuer(v1Form);
521      cert.setSerialNumber(new BigInteger("27"));
522      GregorianCalendar c = new GregorianCalendar();
523      Date notBeforeTime = c.getTime();
524      c.add(Calendar.MONTH, 1);
525      Date notAfterTime = c.getTime();
526      cert.setNotBeforeTime(notBeforeTime);
527      cert.setNotAfterTime(notAfterTime);
528      Attribute[] attributes = new Attribute[1];
529      // just for testing some abritrary attribute
530      SEQUENCE postalAddress = new SEQUENCE();
531      postalAddress.addComponent(new UTF8String("A-8010 Graz, Austria"));
532      postalAddress.addComponent(new UTF8String("Inffeldgasse 16A"));
533      attributes[0] = new Attribute(ObjectID.postalAddress, new ASN1Object[] {postalAddress});
534      cert.setAttributes(attributes);
535      cert.sign((AlgorithmID)AlgorithmID.sha1WithRSAEncryption_.clone(), issuer1_pk);
536      cert.verify(user1Certs[1].getPublicKey());
537      return cert;
538    } catch (Exception ex) {
539      throw new CMSException("Error creating attribute certificate: " + ex.toString());   
540    }    
541  
542  }  
543
544  /**
545   * Starts the demo.
546   */
547  public void start() {
548     // the test message
549    String m = "This is the test message.";
550    System.out.println("Test message: \""+m+"\"");
551    System.out.println();
552    byte[] message = m.getBytes();
553   
554    try {
555      byte[] encoding;
556      byte[] received_message = null;
557      System.out.println("Stream implementation demos");
558      System.out.println("===========================");
559      //
560      // test CMS Implicit SignedDataStream
561      //
562      System.out.println("\nImplicit SignedDataStream demo [create]:\n");
563      encoding = createSignedDataStream(message, SignedDataStream.IMPLICIT);
564      // transmit data
565      System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
566      received_message = getSignedDataStream(encoding, null);
567      System.out.print("\nSigned content: ");
568      System.out.println(new String(received_message));
569      
570      //
571      // test CMS Explicit SignedDataStream
572      //
573      System.out.println("\nExplicit SignedDataStream demo [create]:\n");
574      encoding = createSignedDataStream(message, SignedDataStream.EXPLICIT);
575      // transmit data
576      System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
577      received_message = getSignedDataStream(encoding, message);
578      System.out.print("\nSigned content: ");
579      System.out.println(new String(received_message));
580            
581      // the non-stream implementation
582      System.out.println("\nNon-stream implementation demos");
583      System.out.println("===============================");
584   
585      //
586      // test CMS Implicit SignedData
587      //
588      System.out.println("\nImplicit CMS SignedData demo [create]:\n");
589      encoding = createSignedData(message, SignedData.IMPLICIT);
590      // transmit data
591      System.out.println("\nImplicit CMS SignedData demo [parse]:\n");
592      received_message = getSignedData(encoding, null);
593      System.out.print("\nSigned content: ");
594      System.out.println(new String(received_message));
595
596      //
597      // test CMS Explicit SignedData
598      //
599      System.out.println("\nExplicit CMS SignedData demo [create]:\n");
600      encoding = createSignedData(message, SignedData.EXPLICIT);
601      // transmit data
602      System.out.println("\nExplicit CMS SignedData demo [parse]:\n");
603      received_message = getSignedData(encoding, message);
604      System.out.print("\nSigned content: ");
605      System.out.println(new String(received_message));
606      
607        } catch (Exception ex) {
608          ex.printStackTrace();
609          throw new RuntimeException(ex.toString());
610        }
611  }
612  
613  /**
614   * Creates a SigningCertificate attribute for the given certificates.
615   * 
616   * @param certs the certificates for which to create the SigningCertificate
617   *              attribute
618   *              
619   * @return the SigningCertificate attribute just created             
620   *              
621   * @throws CMSException if an error occurs when creating the
622   *                      SigningCertificate attribute             
623   */
624  protected SigningCertificate createSigningCertificate(Certificate[] certs) 
625    throws CMSException {
626    
627    try {
628      return new SigningCertificate(certs, true);
629    } catch (Exception ex) {
630      throw new CMSException("Error creating SigningCertificate attribute: " + ex.toString());
631    }
632  }
633  
634  /**
635   * Gets the SigningCertificate attribute from the given SignerInfo.
636   * 
637   * @param signerInfo the SignerInfo from which to get the
638   *                   SigningCertificate attribute
639   *                   
640   * @return the SigningCertificate attribute, or <code>null</code>
641   *         if no SigningCertificate attribute is included  
642   *         
643   * @throws CMSException if an error occurs when getting the
644   *                         SigningCertificate attribute                              
645   */
646  protected SigningCertificate getSigningCertificate(SignerInfo signerInfo)
647    throws CMSException {
648    
649    return signerInfo.getSigningCertificateAttribute();
650  }
651  
652  /**
653   * Prints some header lines to System.out.
654   */
655  protected void printHeader() {
656    System.out.println();
657    System.out.println("**********************************************************************************");
658    System.out.println("*                       SigningCertificateDemo demo                              *");
659    System.out.println("*          (shows the usage of the ESS SigningCertificate attribute)             *");
660    System.out.println("**********************************************************************************");
661    System.out.println();
662  }
663  
664  
665  /**
666   * The main method.
667   * 
668   * @throws IOException 
669   *            if an I/O error occurs when reading required keys
670   *            and certificates from files
671   */
672  public static void main(String argv[]) throws Exception {
673
674        DemoUtil.initDemos();
675    (new SigningCertificateDemo()).start();
676    System.out.println("\nReady!");
677    DemoUtil.waitKey();
678  }
679}