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