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.// Copyright (C) 2002 IAIK
057    // https://sic.tech
058    //
059    // Copyright (C) 2003 - 2025 Stiftung Secure Information and 
060    //                           Communication Technologies SIC
061    // https://sic.tech
062    //
063    // All rights reserved.
064    //
065    // This source is provided for inspection purposes and recompilation only,
066    // unless specified differently in a contract with IAIK. This source has to
067    // be kept in strict confidence and must not be disclosed to any third party
068    // under any circumstances. Redistribution in source and binary forms, with
069    // or without modification, are <not> permitted in any case!
070    //
071    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
072    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
073    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
074    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
075    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
076    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
077    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
078    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
079    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
080    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
081    // SUCH DAMAGE.
082    //
083    // $Header: /IAIK-CMS/current/src/demo/smime/ess/SignedReceiptDemo.java 39    12.02.25 17:59 Dbratko $
084    // $Revision: 39 $
085    //
086    
087    package demo.smime.ess;
088    
089    import iaik.asn1.structures.AlgorithmID;
090    import iaik.asn1.structures.Attribute;
091    import iaik.cms.IssuerAndSerialNumber;
092    import iaik.cms.SignerInfo;
093    import iaik.smime.SMimeSignerInfo;
094    import iaik.smime.SignedContent;
095    import iaik.smime.ess.ContentIdentifier;
096    import iaik.smime.ess.ESSException;
097    import iaik.smime.ess.EntityIdentifier;
098    import iaik.smime.ess.MLData;
099    import iaik.smime.ess.MLExpansionHistory;
100    import iaik.smime.ess.MLReceiptPolicy;
101    import iaik.smime.ess.Receipt;
102    import iaik.smime.ess.ReceiptContent;
103    import iaik.smime.ess.ReceiptRequest;
104    import iaik.smime.ess.ReceiptsFrom;
105    import iaik.smime.ess.utils.SenderAndReceiptContentDigest;
106    import iaik.smime.ess.utils.SignedReceipt;
107    import iaik.x509.X509Certificate;
108    
109    import java.io.ByteArrayInputStream;
110    import java.io.ByteArrayOutputStream;
111    import java.io.IOException;
112    import java.io.OutputStream;
113    import java.security.NoSuchAlgorithmException;
114    import java.security.PrivateKey;
115    import java.security.PublicKey;
116    import java.security.SignatureException;
117    import java.util.Date;
118    
119    import javax.mail.Message;
120    import javax.mail.MessagingException;
121    import javax.mail.Session;
122    import javax.mail.internet.InternetAddress;
123    import javax.mail.internet.MimeMessage;
124    
125    import demo.DemoSMimeUtil;
126    import demo.DemoUtil;
127    import demo.keystore.CMSKeyStore;
128    
129    
130    /**
131     * An <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a> ESS ReceiptRequest -- SignedReceipt demo.
132     * <p>
133     * This demo creates a message with a {@link iaik.smime.ess.ReceiptRequest
134     * ReceiptRequest} attribute and "sends" it to some intended recipient.
135     * The recipient then "sends" a signed receipt message back to the
136     * original sender who finally validates the signed receipt.
137     * A further test run adds a MLA layer with a {@link iaik.smime.ess.MLExpansionHistory
138     * MLExpansionHistory} attribute,
139     * that supersedes the original receipt request.
140     * <p>
141     * To run this demo the following packages are required:
142     * <ul>
143     *    <li>
144     *       <code>iaik_cms.jar</code>
145     *    </li>
146     *    <li>
147     *       <code>iaik_jce(_full).jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">IAIK-JCE Core Crypto Library</a>).
148     *    </li>
149     *    <li>
150     *       <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
151     *    </li>   
152     *    <li>
153     *       <code>activation.jar</code> (<a href="http://www.oracle.com/technetwork/java/javase/downloads/index-135046.html" target="_blank">Java Activation Framework</a>; required for JDK versions &lt; 1.6).
154     *    </li> 
155     * </ul>
156     *
157     * @see iaik.smime.ess.ReceiptRequest 
158     * @see iaik.smime.ess.Receipt
159     * @see iaik.smime.ess.MLExpansionHistory
160     * @see iaik.smime.ess.MLData
161     * @see iaik.smime.ess.MLReceiptPolicy
162     * @see iaik.smime.ess.utils.SignedReceipt
163     */
164    public class SignedReceiptDemo {
165        
166      // whether to dump all generates test messages to System.out
167      final static boolean DUMP_MESSAGES = false;  
168     
169      // sender (sends a receipt request message)
170      String senderName_ = "IAIK Demo Sender";
171      // recipient (receives a receipt request and sends a signed receipt)
172      String recipientName_ = "IAIK Demo Recipient";
173      // an mail list agent
174      String mlaName_ = "IAIK Demo ML Agent";
175      // we use the same email address for all parties
176      String senderAddress_ = "\""+senderName_+"\" <smimetest@iaik.at>";
177      String recipientAddress_ = "\""+recipientName_+"\" <smimetest@iaik.at>";
178      String mlaAddress_ = "\""+mlaName_+"\" <smimetest@iaik.tugraz.at>";
179      
180      String host_ = "mailhost";                  // name of the mailhost
181    
182      X509Certificate[] signerCertificates_;      // signer certificate list
183      X509Certificate signerCertificate_;         // certificate of the signer/sender
184      X509Certificate encryptionCertOfSigner_;    // signer uses different certificate for encryption
185      PrivateKey signerPrivateKey_;               // private key of the signer/sender
186      
187      X509Certificate[] recipientCertificates_;   // recipient certificate list
188      X509Certificate recipientCertificate_;      // certificate of the recipient
189      X509Certificate encryptionCertOfRecipient_; // recipient uses different certificate for encryption
190      PrivateKey recipientPrivateKey_;            // private key of the recipient
191      
192      X509Certificate[] signerCertificatesOfMLA_; // signer certificates of MLA
193      PrivateKey signerPrivateKeyOfMLA_;          // signer private key of MLA
194     
195      
196      /**
197       * Empty default constructor. Reads all required keys and certificates
198       * from the demo keystore (created by running @link demo.keystore.SetupCMSKeySrore)
199       * stored at "cms.keystore" in your current working directoy.
200       */
201      public SignedReceiptDemo() {
202        
203        System.out.println();
204        System.out.println("******************************************************************************************");
205        System.out.println("*                               SignedReceiptDemo                                        *");
206        System.out.println("*       (shows the usage of the IAIK-CMS library for handling ESS signed receipts)       *");
207        System.out.println("******************************************************************************************");
208        System.out.println();
209        
210        // get the certificates from the KeyStore
211        signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
212        signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
213        signerCertificate_ = signerCertificates_[0];
214        encryptionCertOfSigner_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
215        recipientCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
216        recipientPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
217        recipientCertificate_ = recipientCertificates_[0];
218        encryptionCertOfRecipient_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
219        signerCertificatesOfMLA_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
220        signerPrivateKeyOfMLA_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
221        X509Certificate[] tmpCerts = new X509Certificate[signerCertificates_.length + 1];
222        System.arraycopy(signerCertificates_, 0, tmpCerts, 0, signerCertificates_.length);
223        tmpCerts[signerCertificates_.length] = encryptionCertOfSigner_;
224        signerCertificates_ = tmpCerts;
225        tmpCerts = new X509Certificate[recipientCertificates_.length + 1];
226        System.arraycopy(recipientCertificates_, 0, tmpCerts, 0, recipientCertificates_.length);
227        tmpCerts[recipientCertificates_.length] = encryptionCertOfRecipient_;
228        recipientCertificates_ = tmpCerts;
229      }
230      
231      /**
232       * Starts the SignedReceipt demo.
233       */
234      public void start() {
235        boolean implicit = true;
236        boolean mla = false;
237        System.out.println("Testing receipt request - signed receipt (all implicit)");
238        test(implicit, mla);
239        mla = true;
240        System.out.println("Testing receipt request - MLA - signed receipt (all implicit)");
241        test(implicit, mla);
242        implicit = false;
243        mla = false;
244        System.out.println("Testing receipt request - signed receipt (all explicit)");
245        test(implicit, mla);
246        mla = true;
247        System.out.println("Testing receipt request - MLA - signed receipt (all explicit)");
248        test(implicit, mla);
249        System.out.println("Ready!");
250      }
251      
252      /**
253       * Runs the ReceiptRequest - SignedReceipt test.
254       *
255       * @param implicit whether to create implicit (application/pkcs7-mime) or
256       *                 explicit (multipart/signed) ReceiptRequest messages
257       * 
258       * @param mla whether to add a MLA layer
259       */
260      public void test(boolean implicit, boolean mla) {
261    
262        try {
263              // get the default Session
264              Session session = DemoSMimeUtil.getSession();
265          
266          Message msg;    // the message to send
267          ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
268          ByteArrayInputStream bais;  // we read from a stream
269          
270          // we send a signed message with a receipt request
271          System.out.println("Creating implicit signed message with receipt request.");
272          // create message and "send" it (write it to baos)
273          msg = createSignedMessageWithReceiptRequest(session, implicit, baos);
274          
275          // now parse the receipt request message
276              bais = new ByteArrayInputStream(baos.toByteArray());
277              msg = new MimeMessage(session, bais);
278              if (DUMP_MESSAGES) {
279                dumpMessage(msg);
280              }
281              baos.reset();
282              
283              if (mla == true) {
284                // MLA receives the message and adds a MLExpansionHistory that supersedes the 
285                // original receipt request
286                System.out.println("MLA: parsing received original message.");
287                SignedContent sc = (SignedContent)msg.getContent();
288                System.out.println("MLA: Verifying signature.");
289                // verify the signature (we assume only one signer)
290            X509Certificate signer = sc.verify();
291                System.out.println("MLA: This message is signed from: "+signer.getSubjectDN());
292                // add MLExpansionHistory
293                System.out.println("MLA: Creating new signed message with MLExpansionHistory attribute.");
294            SignedContent mlaSc = new SignedContent(sc, implicit);
295            mlaSc.setCertificates(signerCertificatesOfMLA_);
296            try {
297              SMimeSignerInfo signerInfo = new SMimeSignerInfo(signerCertificatesOfMLA_[0],
298                                                               (AlgorithmID)AlgorithmID.sha256.clone(), 
299                                                               (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(), 
300                                                               signerPrivateKeyOfMLA_);            
301              // add a MLExpansionHistory attribute superseding the original receipt request
302              MLExpansionHistory mlExpansionHistory = createMLExpansionHistory(signerCertificatesOfMLA_[0],
303                                                                               new Date(),
304                                                                               mlaAddress_);
305              signerInfo.addSignedAttribute(new Attribute(mlExpansionHistory));
306              mlaSc.addSigner(signerInfo);
307            } catch (NoSuchAlgorithmException ex) {
308              throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
309            }
310            msg = createMessage(session, mlaAddress_, recipientAddress_, "IAIK-S/MIME: MLA with ReceiptRequest");
311            msg.setContent(mlaSc, mlaSc.getContentType());
312            // let the SignedContent update some message headers
313            mlaSc.setHeaders(msg);
314            msg.saveChanges();
315                msg.writeTo(baos);
316                
317                // now parse the MLA message
318                bais = new ByteArrayInputStream(baos.toByteArray());
319            msg = new MimeMessage(session, bais);
320              
321                if (DUMP_MESSAGES) {
322                  dumpMessage(msg);
323                }
324          }   
325              
326              // signed receipt creation
327              baos.reset();
328              Message msg1 = createMessageWithSignedReceipt(session, msg);
329              msg1.saveChanges();
330              msg1.writeTo(baos);
331              bais = new ByteArrayInputStream(baos.toByteArray());
332              msg = new MimeMessage(session, bais);
333    
334              if (DUMP_MESSAGES) {
335                dumpMessage(msg);
336              }
337              // signed receipt validation
338              System.out.println("\nNow getting and verifying signed receipt:");
339              verifyReceiptContent(msg);
340          
341            } catch (Exception ex) {
342              ex.printStackTrace();
343              throw new RuntimeException(ex.toString());
344            }
345    
346            
347      }
348      
349      /**
350       * Creates a MimeMessage.
351       *
352       * @param session the current mail session
353       * @param from the sender of the message
354       * @param to the recipient of the message
355       * @param subject the subject of the message
356       *
357       * @return the newly created MimeMessage
358       *
359       * @throws MessagingException if an error occurs when creating the message
360       */
361      public Message createMessage(Session session, String from, String to, String subject) throws MessagingException {
362        MimeMessage msg = new MimeMessage(session);
363        msg.setFrom(new InternetAddress(from));
364            msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to, false));
365            msg.setSentDate(new Date());
366        return msg;
367      }
368    
369      /**
370       * Creates a signed message that contains a <code>ReceiptRequest</code> attribute.
371       *
372       * @param session the current mail session
373       * @param implicit whether to sign the content implicitly or explicitly
374       * @param os the output stream to which to write the message
375       *
376       * @return the message containing a ReceiptRequest attribute
377       */
378      public Message createSignedMessageWithReceiptRequest(Session session,
379                                                           boolean implicit,
380                                                           OutputStream os)
381        throws Exception {
382    
383        Message msg = createMessage(session, senderAddress_, recipientAddress_, "IAIK-S/MIME: ReceiptRequest");
384        
385        // create the inner signed content
386        SignedContent sc = new SignedContent(implicit, implicit ? SignedContent.SIGNED_DATA : null);
387        sc.setText("This is a signed message with a ReceiptRequest.");
388        
389        sc.setCertificates(signerCertificates_);
390        SMimeSignerInfo signerInfo = new SMimeSignerInfo(signerCertificate_, 
391                                                         (AlgorithmID)AlgorithmID.sha256.clone(),
392                                                         (AlgorithmID)AlgorithmID.rsaEncryption.clone(), 
393                                                         signerPrivateKey_, 
394                                                         encryptionCertOfSigner_,
395                                                         true);
396        // add a ReceiptRequest attribute to request a receipt to be sent back to the sender
397        ReceiptRequest receiptRequest = createReceiptRequest(signerCertificate_.getPublicKey(),
398                                                             msg.getSentDate(),
399                                                             senderAddress_);
400        signerInfo.addSignedAttribute(new Attribute(receiptRequest));
401        sc.addSigner(signerInfo);
402        msg.setContent(sc, sc.getContentType());
403        // let the SignedContent update some message headers
404        sc.setHeaders(msg);
405        msg.saveChanges();
406            msg.writeTo(os);
407            // now after sending (writing) the message we can access and keep the digest values for later SignedReceipt validation
408        storeDigestValues(sc);
409        return msg;
410      }
411      
412      /**
413       * Creates a ReceiptRequest attribute to request all recipients to send
414       * a signed receipt to the entity to the given email address.
415       * 
416       * @param publicKey the public key of the sender (used for ContentIdentifier calculation)
417       * @param sentDate the sent date of the message (used for ContentIdentifier calculation)
418       * @param email the email address of the sender (to whom to return a signed receipt)
419       *
420       * @return the ReceiptRequest
421       */
422      public ReceiptRequest createReceiptRequest(PublicKey publicKey, Date sentDate, String email) {
423        // we request a receipt from all recipients
424        ReceiptsFrom receiptsFrom = new ReceiptsFrom(ReceiptsFrom.ALL_RECIPIENTS);
425        // the receipt should be send to the given email
426        String[] sendTo = { email };
427        // create the signed content identifier
428        ContentIdentifier contentIdentifier = new ContentIdentifier(publicKey, sentDate, null);
429        // create the receipt request
430        ReceiptRequest receiptRequest = new ReceiptRequest(contentIdentifier, receiptsFrom, sendTo);
431        return receiptRequest;
432      }  
433      
434      /**
435       * Creates a MLExpansionHistory containing only one MLData for
436       * the given MLA with given expansion time and a MLReceiptPolicy
437       * of type IN_ADDITION_TO for the given mlaEmailAddress.
438       *
439       * @param mlaCertificate the certificate of the MLA from which to create the
440       *        MLData EntityIdentiifier of type IssuerAndSerialNumber
441       * @param expansionTime the expansion time
442       * @param mlaEmailAddress to be set as IN_ADDITION_TO recipient list for
443       *                        the MLData MLRecipientPolicy
444       *
445       * @return the newly created MLExpansionHistory
446       */
447      public static MLExpansionHistory createMLExpansionHistory(X509Certificate mlaCertificate,
448                                                                Date expansionTime,
449                                                                String mlaEmailAddress) {
450        
451        IssuerAndSerialNumber ias = new IssuerAndSerialNumber(mlaCertificate);
452        MLData mlData = new MLData(new EntityIdentifier(ias), expansionTime); 
453        MLReceiptPolicy mlReceiptPolicy = new MLReceiptPolicy(MLReceiptPolicy.IN_ADDITION_TO);
454        mlReceiptPolicy.setRecipientList(new String[] { mlaEmailAddress });
455        mlData.setMLReceiptPolicy(mlReceiptPolicy);
456        return new MLExpansionHistory(mlData);
457      }  
458      
459      /**
460       * Keeps the signature message digest value of the sender and the receipt content digest
461       * values for later SignedReceipt validation.
462       *
463       * @param signedContent the signed message for which to keep the digest values
464       *
465       * @throws ESSException if an error occurs while gathering the required digest values
466       */
467      public void storeDigestValues(SignedContent signedContent) throws ESSException {
468        SignerInfo originatorSignerInfo = signedContent.getSignerInfos()[0];
469        SenderAndReceiptContentDigest.storeEntry(new SenderAndReceiptContentDigest(originatorSignerInfo));
470      }  
471      
472      /**
473       * Creates a signed-receipt message from the received message.
474       *
475       * @param session the current mail session
476       * @param receivedMsg the message containing a ReceiptRequest attribute
477       *
478       * @return a message containing s signed receipt to be sent in return
479       *         to the receipt request
480       *
481       * @throws Exception if some error occurs during receipt request processing
482       *                      or signed receipt creation
483       */
484      public Message createMessageWithSignedReceipt(Session session, Message receivedMsg) 
485              throws Exception {
486    
487        SignedReceipt signedReceipt = new SignedReceipt(receivedMsg, recipientAddress_, System.out);
488        String subject = "IAIK-S/MIME: Signed Receipt";
489             
490        Message msg = signedReceipt.createReceiptMessage(recipientPrivateKey_, 
491                                                         recipientCertificates_,
492                                                         recipientCertificates_[0],
493                                                         (AlgorithmID)AlgorithmID.sha256.clone(),
494                                                         (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
495                                                         encryptionCertOfRecipient_,
496                                                         true,
497                                                         session,
498                                                         subject);
499        return msg;
500      } 
501      
502      
503      /**
504       * Validates a signed receipt message received in return to a receipt request
505       * message.
506       *
507       * @param receiptMsg the message containing the signed receipt
508       *
509       * @throws Exception if the receipt validation fails for some reason
510       */
511      public void verifyReceiptContent(Message receiptMsg) throws Exception {
512         // we assume to already know of the signed content        
513        ReceiptContent receiptContent = (ReceiptContent)receiptMsg.getContent();
514        
515        Receipt receipt = (Receipt)receiptContent.getContent();
516            System.out.println("\nReceipt received:");
517            System.out.println(receipt);
518            
519        
520        // verify the signature (we assume only one signer)
521        X509Certificate receiptSigner = null;
522        try {
523          receiptSigner = receiptContent.verify();
524              System.out.println("This receipt content is signed from: "+receiptSigner.getSubjectDN());
525            } catch (SignatureException ex) {
526              System.err.println("Signature verification error!");
527              throw ex;
528        }
529        
530      
531            try {
532              SenderAndReceiptContentDigest sarcd = SenderAndReceiptContentDigest.validateReceiptContent(receiptContent);
533              // now after validation we may remove the kept digest values from the repository
534              SenderAndReceiptContentDigest.removeEntry(sarcd);
535            } catch (ESSException ex) {
536              System.err.println("Signed Receipt validation error!");
537              throw ex;
538            }    
539            System.out.println("ReceiptContent successful validated!");
540      }  
541      
542      /**
543       * Dumps the given message to System.out.
544       * 
545       * @param msg the message to be dumped
546       *
547       * @throws Exception if some error occurs
548       */
549      private static void dumpMessage(Message msg) throws Exception {
550        System.out.println("******************************************************************");
551        System.out.println("Message dump: \n");
552        msg.writeTo(System.out);
553        System.out.println("******************************************************************");
554      }  
555      
556      /**
557       * Main method.
558       */
559      public static void main(String[] argv) throws IOException {
560        
561        try {
562          DemoSMimeUtil.initDemos();
563              (new SignedReceiptDemo()).start();
564            } catch (Exception ex) {
565          ex.printStackTrace();   
566        }       
567    
568        DemoUtil.waitKey();
569            
570      }
571    }