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