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/pkcs7cms/PKCS7CMSSignedDataDemo.java 24    12.02.25 17:58 Dbratko $
029// $Revision: 24 $
030//
031
032package demo.cms.pkcs7cms;
033
034import iaik.asn1.ASN1Object;
035import iaik.asn1.CodingException;
036import iaik.asn1.ObjectID;
037import iaik.asn1.structures.AlgorithmID;
038import iaik.asn1.structures.Attribute;
039import iaik.asn1.structures.ChoiceOfTime;
040import iaik.asn1.structures.PolicyInformation;
041import iaik.asn1.structures.PolicyQualifierInfo;
042import iaik.cms.CMSException;
043import iaik.cms.IssuerAndSerialNumber;
044import iaik.cms.SignedData;
045import iaik.cms.SignedDataStream;
046import iaik.cms.SignerInfo;
047import iaik.cms.Utils;
048import iaik.pkcs.PKCSException;
049import iaik.smime.ess.SigningCertificate;
050import iaik.utils.CryptoUtils;
051import iaik.utils.Util;
052import iaik.x509.X509Certificate;
053import iaik.x509.attr.AttributeCertificate;
054
055import java.io.ByteArrayInputStream;
056import java.io.ByteArrayOutputStream;
057import java.io.IOException;
058import java.io.InputStream;
059import java.security.NoSuchAlgorithmException;
060import java.security.PrivateKey;
061import java.security.SignatureException;
062import java.security.cert.Certificate;
063
064import demo.DemoUtil;
065import demo.keystore.CMSKeyStore;
066
067
068/**
069 * This class demonstrates the CMS SignedData implementation and checks it
070 * against the IAIK PKCS#7 library.
071 */
072public class PKCS7CMSSignedDataDemo {
073
074  // certificate of user 1
075  X509Certificate user1Cert_;
076  // private key of user 1
077  PrivateKey user1PrivKey_;
078  // certificate of user 2
079  X509Certificate user2Cert_;
080  // private key of user 2
081  PrivateKey user2PrivKey_;
082  // a certificate array containing the user certs + CA
083  X509Certificate[] certificates_;
084  // certificates of user1
085  X509Certificate[] user1Certs_;
086  
087
088  /**
089   * Setup the demo certificate chains.
090   * 
091   * Keys and certificate are retrieved from the demo KeyStore.
092   * 
093   * @throws IOException if an file read error occurs
094   */
095  public PKCS7CMSSignedDataDemo() throws IOException {
096    
097    System.out.println();
098    System.out.println("***********************************************************************************************");
099    System.out.println("*                                 PKCS7CMSSignedDataDemo                                      *");
100    System.out.println("*    (tests the CMS SignedData against the IAIK-JCE PKCS#7 Signedata type implementation)     *");
101    System.out.println("***********************************************************************************************");
102    System.out.println();
103    
104    // add all certificates to the list
105    user1Certs_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
106    user1Cert_ = (X509Certificate)user1Certs_[0];
107    user1PrivKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
108    user2Cert_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1)[0];
109    user2PrivKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
110    
111    certificates_ = new X509Certificate[user1Certs_.length + 1];
112    System.arraycopy(user1Certs_, 0, certificates_, 0, user1Certs_.length);
113    certificates_[user1Certs_.length] = user2Cert_;
114    
115  }
116  
117    
118  
119  /**
120   * Creates a CMS <code>SignedData</code> object.
121   * <p>
122   *
123   * @param message the message to be signed, as byte representation
124   * @param mode the transmission mode, either IMPLICIT or EXPLICIT
125   * 
126   * @return the DER encoding of the <code>SignedData</code> object just created
127   * @throws CMSException if the <code>SignedData</code> object cannot
128   *                          be created
129   * @throws IOException if an I/O error occurs
130   */
131  public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException  {
132    
133    System.out.print("Create a new message signed by user 1 :");
134   
135    // we are testing the stream interface
136    ByteArrayInputStream is = new ByteArrayInputStream(message);
137    // create a new SignedData object which includes the data
138    SignedDataStream signed_data = new SignedDataStream(is, mode);
139    
140    // SignedData shall include the certificate chain for verifying
141    signed_data.setCertificates(certificates_);
142    
143    // cert at index 0 is the user certificate
144    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1Cert_);
145
146    // create a new SignerInfo
147    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_);
148    // create some authenticated attributes
149    // the message digest attribute is automatically added
150    Attribute[] attributes = new Attribute[3];
151    // content type is data
152    attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data});
153    // signing time is now
154    attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()});
155    // signing certificate
156    try {
157      SigningCertificate signingCertificate = new SigningCertificate(user1Certs_, true);
158      String explicitText = "This certificate only may be used for test purposes";
159      PolicyQualifierInfo policyQualifier = new PolicyQualifierInfo(null, null, explicitText);
160      PolicyInformation[] policyInformations = 
161        { new PolicyInformation(new ObjectID("1.3.6.1.4.1.2706.17.0.11.1.1"),
162                              new PolicyQualifierInfo[] { policyQualifier }) };
163      signingCertificate.setPolicies(policyInformations);                        
164      System.out.println("Include signingCertificate attribute:");
165      System.out.println(signingCertificate);
166      attributes[2] = new Attribute(ObjectID.signingCertificate, new ASN1Object[] {signingCertificate.toASN1Object()});
167    } catch (Exception ex) {
168      throw new CMSException("Cannot create SigningCertificate attribute: " + ex.getMessage());   
169    }    
170    // set the attributes
171    signer_info.setSignedAttributes(attributes);
172    // finish the creation of SignerInfo by calling method addSigner
173    try {
174      signed_data.addSignerInfo(signer_info);
175      // another SignerInfo without signed attributes 
176      signer_info = new SignerInfo(new IssuerAndSerialNumber(user2Cert_), 
177          (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_);
178      
179      
180      // the message digest itself is protected
181      signed_data.addSignerInfo(signer_info);
182
183    } catch (NoSuchAlgorithmException ex) {
184      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
185    } 
186
187    // write the data through SignedData to any out-of-band place
188    if (mode == SignedDataStream.EXPLICIT) {
189      InputStream data_is = signed_data.getInputStream();
190      byte[] buf = new byte[1024];
191      int r;
192      while ((r = data_is.read(buf)) > 0) {
193        ;   // skip data
194      }   
195    }
196
197    // return the SignedData as DER encoded byte array with block size 2048
198    ByteArrayOutputStream os = new ByteArrayOutputStream();
199    signed_data.writeTo(os, 2048);
200    return os.toByteArray();
201  }
202  
203  /**
204   * Creates a PKCS#7 <code>SignedData</code> object.
205   * <p>
206   *
207   * @param message the message to be signed, as byte representation
208   * @param mode the transmission mode, either IMPLICIT or EXPLICIT
209   * @return the DER encoding of the <code>SignedData</code> object just created
210   * @throws PKCSException if the <code>SignedData</code> object cannot
211   *                          be created
212   * @throws IOException if an I/O error occurs
213   */
214  public byte[] createPKCS7SignedDataStream(byte[] message, int mode) throws iaik.pkcs.PKCSException, IOException  {
215    
216     // we are testing the stream interface
217    ByteArrayInputStream is = new ByteArrayInputStream(message);
218    // create a new SignedData object which includes the data
219    iaik.pkcs.pkcs7.SignedDataStream signed_data = new iaik.pkcs.pkcs7.SignedDataStream(is, mode);
220    // SignedData shall include the certificate chain for verifying
221    signed_data.setCertificates(certificates_);
222    
223    // cert at index 0 is the user certificate
224    iaik.pkcs.pkcs7.IssuerAndSerialNumber issuer = new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user1Cert_);
225
226    // create a new SignerInfo
227    iaik.pkcs.pkcs7.SignerInfo signer_info = new iaik.pkcs.pkcs7.SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_);
228    // create some authenticated attributes
229    // the message digest attribute is automatically added
230    Attribute[] attributes = new Attribute[2];
231    // content type is data
232    attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data});
233    // signing time is now
234    attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()});
235    // set the attributes
236    signer_info.setAuthenticatedAttributes(attributes);
237    // finish the creation of SignerInfo by calling method addSigner
238    try {
239      signed_data.addSignerInfo(signer_info);
240
241      // another SignerInfo without authenticated attributes 
242      signer_info = new iaik.pkcs.pkcs7.SignerInfo(new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user2Cert_), 
243          (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_);
244      // the message digest itself is protected
245      signed_data.addSignerInfo(signer_info);
246
247    } catch (NoSuchAlgorithmException ex) {
248      throw new iaik.pkcs.PKCSException("No implementation for signature algorithm: "+ex.getMessage());
249    }
250
251    // write the data through SignedData to any out-of-band place
252    if (mode == iaik.pkcs.pkcs7.SignedDataStream.EXPLICIT) {
253      InputStream data_is = signed_data.getInputStream();
254      byte[] buf = new byte[1024];
255      int r;
256      while ((r = data_is.read(buf)) > 0) {
257        ;   // skip data
258      } 
259    }
260
261    // return the SignedData as DER encoded byte array with block size 2048
262    ByteArrayOutputStream os = new ByteArrayOutputStream();
263    signed_data.writeTo(os, 2048);
264    return os.toByteArray();
265  }
266
267  /**
268   * Parses a CMS or PKCS#7 <code>SignedData</code> object and verifies the signatures
269   * for all participated signers.
270   *
271   * @param signedData <code>SignedData</code> object as DER encoded byte array
272   * @param message the the message which was transmitted out-of-band (explicit signed)
273   *
274   * @return the inherent message as byte array
275   * @throws CMSException if any signature does not verify
276   * @throws IOException if an I/O error occurs
277   */
278  public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException {
279
280    // we are testing the stream interface
281    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
282    // create the SignedData object
283    SignedDataStream signed_data = null;
284    if (message == null) {
285      // implicitly signed; read the DER encoded object
286      signed_data = new SignedDataStream(is);
287    }
288    else {
289      // explicitly signed; set the data stream for digesting the message
290      AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() };
291      signed_data = new SignedDataStream(new ByteArrayInputStream(message), algIDs);
292      
293    }
294
295    // get an InputStream for reading the signed content
296    InputStream data = signed_data.getInputStream();
297    ByteArrayOutputStream os = new ByteArrayOutputStream();
298    Util.copyStream(data, os, null);
299    
300    if (message != null) {
301      // if explicitly signed read now the DER encoded object
302      // an explicit S/MIME signed message also consits of message|signature
303      signed_data.decode(is);
304    }
305
306    System.out.println("SignedData contains the following signer information:");
307    SignerInfo[] signer_infos = signed_data.getSignerInfos();
308    
309    int numberOfSignerInfos = signer_infos.length;
310    if (numberOfSignerInfos == 0) {
311      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
312      System.err.println(warning);
313      throw new CMSException(warning);
314    } else {
315      for (int i = 0; i < numberOfSignerInfos; i++) {
316        
317        try {
318          // verify the signed data using the SignerInfo at index i
319          X509Certificate signer_cert = signed_data.verify(i);
320          // if the signature is OK the certificate of the signer is returned
321          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
322          Attribute signingTime = signer_infos[i].getSignedAttribute(ObjectID.signingTime);
323          if (signingTime != null) {
324            ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]);
325            System.out.println("This message has been signed at " + cot.getDate());
326          } 
327          Attribute contentType = signer_infos[i].getSignedAttribute(ObjectID.contentType);
328          if (contentType != null) {
329            System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]);
330          }
331          // check SigningCertificate attribute
332          try {
333            SigningCertificate signingCertificate = signer_infos[i].getSigningCertificateAttribute();
334            if (signingCertificate != null) {
335              System.out.println("SigningCertificate attribute included!");
336              if (!signingCertificate.isSignerCertificate(signer_cert)) {
337                throw new CMSException("Cert ERROR!!! The certificate used for signing is not the one " +
338                                       "identified by the SignerCertificate attribute!");
339              } else {
340                System.out.println("SigningCertificate attribute: Signer cert ok!");   
341              } 
342              // get the authorization certs for this signerInfo
343              Certificate[] authCerts = 
344                 signingCertificate.getAuthorizedCertificates(signed_data.getCertificates());
345              if (authCerts != null) {
346                System.out.println("SignedData contains the following authorization certs for SignerInfo No " + (i+1) +":");   
347                for (int j = 0; j < authCerts.length; j++) {
348                  if (authCerts[j].getType().equalsIgnoreCase("X.509")) {
349                    System.out.println("X.509 public key cert: " + ((X509Certificate)authCerts[j]).getSubjectDN());
350                  } else {
351                    System.out.println("X.509 attribute cert: " + ((AttributeCertificate)authCerts[j]).getHolder());  
352                  }     
353                }  
354              } 
355              if (signingCertificate.countPolicies() > 0) {
356                // get the certs with PolicyInformations according to the SigningCertificate attribute:
357                Certificate[] policyCerts = 
358                   signingCertificate.getPolicyInformationCerts(signed_data.getCertificates());
359                if (policyCerts != null) {
360                  System.out.println("SignedData contains the following certs corresponding to policy informations of SignerInfo No " + (i+1) +":");   
361                  for (int j = 0; j < policyCerts.length; j++) {
362                    if (policyCerts[j].getType().equalsIgnoreCase("X.509")) {
363                      System.out.println("X.509 public key cert: " + ((X509Certificate)policyCerts[j]).getSubjectDN());
364                    } else {
365                      System.out.println("X.509 attribute cert: " + ((AttributeCertificate)policyCerts[j]).getHolder());  
366                    }     
367                  }  
368                }
369              }  
370            }  
371          } catch (NoSuchAlgorithmException ex) {
372            throw new CMSException("Cannot check SigningCertificate attribute: Algorithm SHA not implemented!");
373          } catch (CMSException ex) {
374            throw new CMSException("Error parsing SigningCertificate attribute: " + ex.getMessage());   
375          }    
376         
377        } catch (SignatureException ex) {
378          // if the signature is not OK a SignatureException is thrown
379          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
380          throw new CMSException(ex.toString());
381        } catch (CodingException ex) {
382          throw new CMSException("Attribute decoding error: " + ex.toString()); 
383        }  
384      }  
385    
386      // now check alternative signature verification
387      System.out.println("Now check the signature assuming that no certs have been included:");
388      try {
389         SignerInfo signer_info = signed_data.verify(user1Cert_);
390          // if the signature is OK the certificate of the signer is returned
391          System.out.println("Signature OK from signer: "+user1Cert_.getSubjectDN());
392          
393      } catch (SignatureException ex) {
394          // if the signature is not OK a SignatureException is thrown
395          System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN());
396          throw new CMSException(ex.toString());
397      }
398      
399      
400      try {
401         SignerInfo signer_info = signed_data.verify(user2Cert_);
402          // if the signature is OK the certificate of the signer is returned
403          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
404          
405      } catch (SignatureException ex) {
406          // if the signature is not OK a SignatureException is thrown
407          System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN());
408          throw new CMSException(ex.toString());
409      }
410      // in practice we also would validate the signer certificate(s)  
411    }      
412        
413    return os.toByteArray();
414  }
415  
416    
417  /**
418   * Parses a PKCS#7 <code>SignedData</code> object and verifies the signatures
419   * for all participated signers.
420   *
421   * @param signedData <code>SignedData</code> object as DER encoded byte array
422   * @param message the the message which was transmitted out-of-band (explicit signed)
423   *
424   * @return the inherent message as byte array
425   * @throws iaik.pkcs.PKCSException if any signature does not verify
426   * @throws IOException if an I/O error occurs
427   */
428  public byte[] getPKCS7SignedDataStream(byte[] signedData, byte[] message) throws iaik.pkcs.PKCSException, IOException {
429
430    // we are testing the stream interface
431    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
432    // create the SignedData object
433    iaik.pkcs.pkcs7.SignedDataStream signed_data = null;
434    if (message == null) {
435      // implicitly signed; read the DER encoded object
436      signed_data = new iaik.pkcs.pkcs7.SignedDataStream(is);
437    }
438    else {
439      // explicitly signed; set the data stream for digesting the message
440      AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() };
441      signed_data = new iaik.pkcs.pkcs7.SignedDataStream(new ByteArrayInputStream(message), algIDs);
442      
443    }
444
445    // get an InputStream for reading the signed content
446    InputStream data = signed_data.getInputStream();
447    ByteArrayOutputStream os = new ByteArrayOutputStream();
448    Util.copyStream(data, os, null);
449    
450    if (message != null) {
451      // if explicitly signed read now the DER encoded object
452      // an explicit S/MIME signed message also consits of message|signature
453      signed_data.decode(is);
454    }
455
456    System.out.println("SignedData contains the following signer information:");
457    iaik.pkcs.pkcs7.SignerInfo[] signer_infos = signed_data.getSignerInfos();
458    
459    int numberOfSignerInfos = signer_infos.length;
460    if (numberOfSignerInfos == 0) {
461      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
462      System.err.println(warning);
463      throw new PKCSException(warning);
464    } else {
465      for (int i = 0; i < numberOfSignerInfos; i++) {
466        try {
467          // verify the signed data using the SignerInfo at index i
468          X509Certificate signer_cert = signed_data.verify(i);
469          // if the signature is OK the certificate of the signer is returned
470          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
471          Attribute signingTime = signer_infos[i].getAuthenticatedAttribute(ObjectID.signingTime);
472          if (signingTime != null) {
473            ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]);
474            System.out.println("This message has been signed at " + cot.getDate());
475          } 
476          Attribute contentType = signer_infos[i].getAuthenticatedAttribute(ObjectID.contentType);
477          if (contentType != null) {
478            System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]);
479          }  
480          
481        } catch (SignatureException ex) {
482          // if the signature is not OK a SignatureException is thrown
483          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN());
484          throw new iaik.pkcs.PKCSException(ex.toString());
485        } catch (CodingException ex) {
486          throw new iaik.pkcs.PKCSException("Attribute decoding error: " + ex.toString()); 
487        }  
488      }  
489      // now check alternative signature verification
490      System.out.println("Now check the signature assuming that no certs have been included:");
491      try {
492         iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user1Cert_);
493          // if the signature is OK the certificate of the signer is returned
494          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN());
495          
496      } catch (SignatureException ex) {
497          // if the signature is not OK a SignatureException is thrown
498          System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN());
499          throw new iaik.pkcs.PKCSException(ex.toString());
500      }
501         
502      try {
503         iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user2Cert_);
504          // if the signature is OK the certificate of the signer is returned
505          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN());
506          
507      } catch (SignatureException ex) {
508          // if the signature is not OK a SignatureException is thrown
509          System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN());
510          throw new iaik.pkcs.PKCSException(ex.toString());
511      }
512      // in practice we also would validate the signer certificate(s)  
513    }      
514    
515    return os.toByteArray();
516  }
517  
518  
519  
520  /**
521   * Creates a CMS <code>SignedData</code> object.
522   * <p>
523   *
524   * @param message the message to be signed, as byte representation
525   * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
526   * 
527   * @return the <code>SignedData</code> object as ASN.1 object
528   * @throws CMSException if the <code>SignedData</code> object cannot
529   *                          be created
530   * @throws IOException if an I/O error occurs
531   */
532  public ASN1Object createSignedData(byte[] message, int mode) throws CMSException, IOException  {
533    
534    System.out.println("Create a new message signed by user 1 :");
535  
536    // create a new SignedData object which includes the data
537    SignedData signed_data = new SignedData(message, mode);
538    
539    // SignedData shall include the certificate chain for verifying
540    signed_data.setCertificates(certificates_);
541       
542    // cert at index 0 is the user certificate
543    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1Cert_);
544
545    // create a new SignerInfo
546    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_);
547    // create some authenticated attributes
548    // the message digest attribute is automatically added
549    Attribute[] attributes = new Attribute[3];
550    // content type is data
551    attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data});
552    // signing time is now
553    attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()});
554    // signing certificate
555    SigningCertificate signingCertificate = Utils.makeSigningCertificate(user1Certs_, null, true);
556    System.out.println("Include signingCertificate attribute:");
557    System.out.println(signingCertificate);
558    attributes[2] = new Attribute(ObjectID.signingCertificate, new ASN1Object[] {signingCertificate.toASN1Object()});
559    // set the attributes
560    signer_info.setSignedAttributes(attributes);
561    // finish the creation of SignerInfo by calling method addSigner
562    try {
563      signed_data.addSignerInfo(signer_info);
564
565      // another SignerInfo without signed attributes
566      signer_info = new SignerInfo(new IssuerAndSerialNumber(user2Cert_), 
567          (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_);
568      signed_data.addSignerInfo(signer_info);
569
570    } catch (NoSuchAlgorithmException ex) {
571      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
572    }     
573
574    
575    return signed_data.toASN1Object();
576  }
577  
578  
579  /**
580   * Creates a PKCS#7 <code>SignedData</code> object.
581   * <p>
582   *
583   * @param message the message to be signed, as byte representation
584   * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
585   * @return the <code>SignedData</code> object as ASN.1 object
586   * @throws iaik.pkcs.PKCSException if the <code>SignedData</code> object cannot
587   *                          be created
588   * @throws IOException if an I/O error occurs
589   */
590  public ASN1Object createPKCS7SignedData(byte[] message, int mode) throws iaik.pkcs.PKCSException, IOException  {
591
592    System.out.println("Create a new message signed by user 1 and 2:");
593    
594
595    // create a new SignedData object which includes the data
596    iaik.pkcs.pkcs7.SignedData signed_data = new iaik.pkcs.pkcs7.SignedData(message, mode);
597    // SignedData shall include the certificate chain for verifying
598    signed_data.setCertificates(certificates_);
599    
600    // cert at index 0 is the user certificate
601    iaik.pkcs.pkcs7.IssuerAndSerialNumber issuer = new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user1Cert_);
602
603    // create a new SignerInfo
604    iaik.pkcs.pkcs7.SignerInfo signer_info = new iaik.pkcs.pkcs7.SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_);
605    // create some authenticated attributes
606    // the message digest attribute is automatically added
607    Attribute[] attributes = new Attribute[2];
608    // content type is data
609    attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data});
610    // signing time is now
611    attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()});
612    // set the attributes
613    signer_info.setAuthenticatedAttributes(attributes);
614    // finish the creation of SignerInfo by calling method addSigner
615    try {
616      signed_data.addSignerInfo(signer_info);
617
618      // another SignerInfo without authenticated attributes and
619      signer_info = new iaik.pkcs.pkcs7.SignerInfo(new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user2Cert_), 
620          (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_);
621      // the message digest itself is protected
622      signed_data.addSignerInfo(signer_info);
623
624    } catch (NoSuchAlgorithmException ex) {
625      throw new iaik.pkcs.PKCSException("No implementation for signature algorithm: "+ex.getMessage());
626    }
627
628    
629    return signed_data.toASN1Object();
630  }
631
632
633  /**
634   * Parses a CMS or PKCS#7 <code>SignedData</code> object and verifies the signatures
635   * for all participated signers.
636   *
637   * @param obj <code>SignedData</code> object in ASN.1 representation
638   * @param message the the message which was transmitted out-of-band (explicit signed)
639   *
640   * @return the inherent message as byte array
641   * @throws CMSException if any signature does not verify
642   * @throws IOException if an I/O error occurs
643   */
644  public byte[] getSignedData(ASN1Object obj, byte[] message) throws CMSException, IOException {
645
646    // create the SignedData object
647    SignedData signed_data = null;
648    if (message == null) {
649      // implicitly signed; read the DER encoded object
650      signed_data = new SignedData(obj);
651    }
652    else {
653      // explicitly signed; set the data stream for digesting the message
654      AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() };
655      try {
656         signed_data = new SignedData(message, algIDs);
657      } catch (NoSuchAlgorithmException ex) {
658         throw new CMSException(ex.getMessage());
659      }  
660    }
661
662    // get an InputStream for reading the signed content
663    InputStream data = signed_data.getInputStream();
664    ByteArrayOutputStream os = new ByteArrayOutputStream();
665    Util.copyStream(data, os, null);
666    
667    if (message != null) {
668      // if explicitly signed read now the DER encoded object
669      // an explicit S/MIME signed message also consists of message|signature
670      signed_data.decode(obj);
671    }
672    
673    System.out.println("SignedData contains the following signer information:");
674    SignerInfo[] signer_infos = signed_data.getSignerInfos();
675    
676    int numberOfSignerInfos = signer_infos.length;
677    if (numberOfSignerInfos == 0) {
678      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
679      System.err.println(warning);
680      throw new CMSException(warning);
681    } else {
682      for (int i = 0; i < numberOfSignerInfos; i++) {
683        try {
684          // verify the signed data using the SignerInfo at index i
685          X509Certificate signer_cert = signed_data.verify(i);
686          // if the signature is OK the certificate of the signer is returned
687          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
688          Attribute signingTime = signer_infos[i].getSignedAttribute(ObjectID.signingTime);
689          if (signingTime != null) {
690            ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]);
691            System.out.println("This message has been signed at " + cot.getDate());
692          } 
693          Attribute contentType = signer_infos[i].getSignedAttribute(ObjectID.contentType);
694          if (contentType != null) {
695            System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]);
696          }  
697          Attribute signingCertificateAttr = signer_infos[i].getSignedAttribute(ObjectID.signingCertificate);
698          if (signingCertificateAttr != null) {
699            System.out.println("SigningCertificate attribute included in this SignerInfo.");
700            SigningCertificate signingCertificate = new SigningCertificate(signingCertificateAttr.getValue()[0]);
701            byte[] certHash;
702            try {
703              certHash = signer_cert.getFingerprint("SHA");
704              if (!CryptoUtils.equalsBlock(certHash, signingCertificate.getESSCertIDs()[0].getCertHash())) {
705                System.out.println("Cert ERROR!!! The certificate used for signing is not the one " +
706                                  "identified by the SignerCertificate attribute!");
707                                  
708              } else {
709                System.out.println("SigningCertificate cert hash of Signer cert ok!");   
710              }    
711            } catch (NoSuchAlgorithmException ex) {
712              throw new CMSException("Cannot check SigningCertificate: Algorithm SHA not implemented!");
713            }  
714          }
715        } catch (SignatureException ex) {
716          // if the signature is not OK a SignatureException is thrown
717          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
718          throw new CMSException(ex.toString());
719        } catch (CodingException ex) {
720          throw new CMSException("Attribute decoding error: " + ex.toString()); 
721        } 
722      }      
723    
724      // now check alternative signature verification
725      System.out.println("Now check the signature assuming that no certs have been included:");
726      try {
727         SignerInfo signer_info = signed_data.verify(user1Cert_);
728          // if the signature is OK the certificate of the signer is returned
729          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
730          
731      } catch (SignatureException ex) {
732          // if the signature is not OK a SignatureException is thrown
733          System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN());
734          throw new CMSException(ex.toString());
735      }
736           
737      try {
738         SignerInfo signer_info = signed_data.verify(user2Cert_);
739          // if the signature is OK the certificate of the signer is returned
740          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
741          
742      } catch (SignatureException ex) {
743          // if the signature is not OK a SignatureException is thrown
744          System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN());
745          throw new CMSException(ex.toString());
746      }    
747      
748      // in practice we also would validate the signer certificate(s)  
749    }
750    return signed_data.getContent();
751  }
752  
753  /**
754   * Parses a PKCS#7 <code>SignedData</code> object and verifies the signatures
755   * for all participated signers.
756   *
757   * @param obj <code>SignedData</code> object in ASN.1 representation
758   * @param message the the message which was transmitted out-of-band (explicit signed)
759   *
760   * @return the inherent message as byte array
761   * @throws PKCSException if any signature does not verify
762   * @throws IOException if an I/O error occurs
763   */
764  public byte[] getPKCS7SignedData(ASN1Object obj, byte[] message) throws iaik.pkcs.PKCSException, IOException {
765
766    // create the SignedData object
767    iaik.pkcs.pkcs7.SignedData signed_data = null;
768    if (message == null) {
769      // implicitly signed; read the DER encoded object
770      signed_data = new iaik.pkcs.pkcs7.SignedData(obj);
771    }
772    else {
773      // explicitly signed; set the data stream for digesting the message
774      AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() };
775      try {
776         signed_data = new iaik.pkcs.pkcs7.SignedData(message, algIDs);
777      } catch (NoSuchAlgorithmException ex) {
778         throw new iaik.pkcs.PKCSException(ex.toString());
779      }  
780    }
781
782    // get an InputStream for reading the signed content
783    InputStream data = signed_data.getInputStream();
784    ByteArrayOutputStream os = new ByteArrayOutputStream();
785    Util.copyStream(data, os, null);
786
787    if (message != null) {
788      // if explicitly signed read now the DER encoded object
789      // an explicit S/MIME signed message also consits of message|signature
790      signed_data.decode(obj);
791    }
792    
793    System.out.println("SignedData contains the following signer information:");
794    iaik.pkcs.pkcs7.SignerInfo[] signer_infos = signed_data.getSignerInfos();
795    
796    int numberOfSignerInfos = signer_infos.length;
797    if (numberOfSignerInfos == 0) {
798      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
799      System.err.println(warning);
800      throw new PKCSException(warning);
801    } else {
802      for (int i = 0; i < numberOfSignerInfos; i++) {
803        try {
804          // verify the signed data using the SignerInfo at index i
805          X509Certificate signer_cert = signed_data.verify(i);
806          // if the signature is OK the certificate of the signer is returned
807          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
808          Attribute signingTime = signer_infos[i].getAuthenticatedAttribute(ObjectID.signingTime);
809          if (signingTime != null) {
810            ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]);
811            System.out.println("This message has been signed at " + cot.getDate());
812          } 
813          Attribute contentType = signer_infos[i].getAuthenticatedAttribute(ObjectID.contentType);
814          if (contentType != null) {
815            System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]);
816          }  
817        } catch (SignatureException ex) {
818           // if the signature is not OK a SignatureException is thrown
819           System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN());
820           throw new iaik.pkcs.PKCSException(ex.toString());
821        } catch (CodingException ex) {
822           throw new iaik.pkcs.PKCSException("Attribute decoding error: " + ex.toString()); 
823        } 
824      }      
825    
826      // now check alternative signature verification
827      System.out.println("Now check the signature assuming that no certs have been included:");
828      try {
829         iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user1Cert_);
830          // if the signature is OK the certificate of the signer is returned
831          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN());
832          
833      } catch (SignatureException ex) {
834          // if the signature is not OK a SignatureException is thrown
835          System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN());
836          throw new iaik.pkcs.PKCSException(ex.toString());
837      }
838         
839      try {
840         iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user2Cert_);
841          // if the signature is OK the certificate of the signer is returned
842          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN());
843          
844      } catch (SignatureException ex) {
845          // if the signature is not OK a SignatureException is thrown
846          System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN());
847          throw new iaik.pkcs.PKCSException(ex.toString());
848      }    
849      // in practice we also would validate the signer certificate(s)  
850    }
851    
852    return signed_data.getContent();
853  }
854  
855  
856  /**
857   * Tests the CMS SignedData implementation and checks it against the
858   * IAIK PKCS#7 library.
859   */
860  public void start() {
861     // the test message
862    String m = "This is the test message.";
863    System.out.println("Test message: \""+m+"\"");
864    System.out.println();
865    byte[] message = m.getBytes();
866   
867    try {
868      byte[] data;
869      byte[] received_message = null;
870      System.out.println("Stream implementation demos");
871      System.out.println("===========================");
872     
873      
874      
875      System.out.println("\nChecking against PKCS#7...");
876      
877      //
878      // Implicit SignedDataStream: CMS (create), PKCS#7 (parse)
879      //
880      System.out.println("\nCreating implicit CMS SignedDataStream: \n");
881      data = createSignedDataStream(message, SignedDataStream.IMPLICIT);
882      // transmit data
883      System.out.println("\nParse implicit CMS SignedDataStream with PKCS#7:\n");
884      received_message = getPKCS7SignedDataStream(data, null);
885      System.out.print("\nSigned content: ");
886      System.out.println(new String(received_message));
887      
888      //
889      // Explicit SignedDataStream: CMS (create), PKCS#7 (parse)
890      //
891      System.out.println("\nCreating explicit CMS SignedDataStream: \n");
892      data = createSignedDataStream(message, SignedDataStream.EXPLICIT);
893      // transmit data
894      System.out.println("\nParse explicit CMS SignedDataStream SignedDataStream with PKCS#7:\n");
895      received_message = getPKCS7SignedDataStream(data, message);
896      System.out.print("\nSigned content: ");
897      System.out.println(new String(received_message));
898      
899      //
900      // Implicit SignedDataStream: PKCS#7 (create), CMS (parse)
901      //
902      System.out.println("\nCreating implicit PKCS#7 SignedDataStream: \n");
903      data = createPKCS7SignedDataStream(message, SignedDataStream.IMPLICIT);
904      // transmit data
905      System.out.println("\nParse implicit PKCS#7 SignedDataStream with CMS:\n");
906      received_message = getSignedDataStream(data, null);
907      System.out.print("\nSigned content: ");
908      System.out.println(new String(received_message));
909      
910      //
911      // Explicit SignedDataStream: CMS (create), PKCS#7 (parse)
912      //
913      System.out.println("\nCreating explicit CMS SignedDataStream: \n");
914      data = createSignedDataStream(message, SignedDataStream.EXPLICIT);
915      // transmit data
916      System.out.println("\nParse explicit SignedDataStream SignedDataStream with PKCS#7:\n");
917      received_message = getPKCS7SignedDataStream(data, message);
918      System.out.print("\nSigned content: ");
919      System.out.println(new String(received_message));
920      
921       
922      // the non-stream implementation
923      System.out.println("\nNon-stream implementation demos");
924      System.out.println("===============================");
925   
926      //
927      // test PKCS#7 Data
928      //
929      ASN1Object obj = null;
930
931      System.out.println("\nChecking against PKCS#7...");
932      
933      //
934      // Implicit SignedData: CMS (create), PKCS#7 (parse)
935      //
936      System.out.println("\nCreating implicit CMS SignedData: \n");
937       obj = createSignedData(message, SignedData.IMPLICIT);
938      // transmit data
939      System.out.println("\nParsing implicit CMS SignedData with PKCS#7:\n");
940      received_message = getPKCS7SignedData(obj, null);
941      System.out.print("\nSigned content: ");
942      System.out.println(new String(received_message));
943
944      //
945      // Explicit SignedData: CMS (create), PKCS#7 (parse)
946      //
947      System.out.println("\nCreating explicit CMS SignedData: \n");
948      obj = createSignedData(message, SignedData.EXPLICIT);
949      // transmit data
950      System.out.println("\nParsing explicit CMS SignedData with PKCS#7:\n");
951      received_message = getPKCS7SignedData(obj, message);
952      System.out.print("\nSigned content: ");
953      System.out.println(new String(received_message));
954      
955      
956      //
957      // Implicit SignedData: PKCS#7 (create), CMS (parse)
958      //
959      System.out.println("\nCreating implicit PCSK#7 SignedData: \n");
960       obj = createPKCS7SignedData(message, SignedData.IMPLICIT);
961      // transmit data
962      System.out.println("\nParsing implicit PKCS#7 SignedData with CMS:\n");
963      received_message = getSignedData(obj, null);
964      System.out.print("\nSigned content: ");
965      System.out.println(new String(received_message));
966
967      //
968      // Explicit SignedData: PKCS#7 (create), CMS (parse)
969      //
970      System.out.println("\nCreating implicit PKCS#7 SignedData: \n");
971      obj = createPKCS7SignedData(message, SignedData.EXPLICIT);
972      // transmit data
973      System.out.println("\nParsing explicit PKCS#7 SignedData with CMS:\n");
974      received_message = getSignedData(obj, message);
975      System.out.print("\nSigned content: ");
976      System.out.println(new String(received_message));
977
978        } catch (Exception ex) {
979          ex.printStackTrace();
980          throw new RuntimeException(ex.toString());
981        }
982  }
983  
984  /**
985   * Main method. 
986   *
987   * @throws IOException 
988   *            if an I/O error occurs when reading required keys
989   *            and certificates from files
990   */
991  public static void main(String argv[]) throws Exception {
992
993        DemoUtil.initDemos();
994    
995    (new PKCS7CMSSignedDataDemo()).start();
996    System.out.println("\nReady!");
997    DemoUtil.waitKey();
998  }
999}