001// Copyright (C) 2002 IAIK
002// https://sic.tech/
003//
004// Copyright (C) 2003 - 2025 Stiftung Secure Information and 
005//                           Communication Technologies SIC
006// https://sic.tech/
007//
008// All rights reserved.
009//
010// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/smime/ess/MLADemo.java 49    12.02.25 17:59 Dbratko $
029// $Revision: 49 $
030//
031
032package demo.smime.ess;
033
034import iaik.asn1.CodingException;
035import iaik.asn1.structures.AlgorithmID;
036import iaik.asn1.structures.Attribute;
037import iaik.asn1.structures.Attributes;
038import iaik.cms.CMSSignatureException;
039import iaik.cms.IssuerAndSerialNumber;
040import iaik.cms.KeyTransRecipientInfo;
041import iaik.cms.RecipientInfo;
042import iaik.cms.SignerInfo;
043import iaik.cms.Utils;
044import iaik.smime.EncryptedContent;
045import iaik.smime.SMimeBodyPart;
046import iaik.smime.SMimeException;
047import iaik.smime.SMimeMultipart;
048import iaik.smime.SMimeUtil;
049import iaik.smime.SignedContent;
050import iaik.smime.ess.ESSException;
051import iaik.smime.ess.EntityIdentifier;
052import iaik.smime.ess.MLData;
053import iaik.smime.ess.MLExpansionHistory;
054import iaik.smime.ess.MLReceiptPolicy;
055import iaik.smime.ess.utils.ESSLayerException;
056import iaik.smime.ess.utils.ESSLayers;
057import iaik.smime.ess.utils.KeyStoreDatabase;
058import iaik.smime.ess.utils.MLA;
059import iaik.utils.CryptoUtils;
060import iaik.x509.X509Certificate;
061
062import java.io.ByteArrayInputStream;
063import java.io.ByteArrayOutputStream;
064import java.io.IOException;
065import java.security.NoSuchAlgorithmException;
066import java.security.PrivateKey;
067import java.util.Date;
068
069import jakarta.activation.DataHandler;
070import jakarta.activation.DataSource;
071import jakarta.activation.FileDataSource;
072import jakarta.mail.BodyPart;
073import jakarta.mail.Message;
074import jakarta.mail.MessagingException;
075import jakarta.mail.Multipart;
076import jakarta.mail.Session;
077import jakarta.mail.internet.InternetAddress;
078import jakarta.mail.internet.MimeBodyPart;
079import jakarta.mail.internet.MimeMessage;
080
081import demo.DemoSMimeUtil;
082import demo.DemoUtil;
083import demo.keystore.CMSKeyStore;
084import demo.keystore.CMSKeyStoreConstants;
085
086/**
087 * A ESS mailing list agent (MLA) demo.
088 * Demonstrates the usage of the {@link iaik.smime.ess.utils.MLA MLA} utility by
089 * means of the examples given in <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>,
090 * section 4.2.1:
091 * <pre>
092 * 4.2.1 Examples of Rule Processing
093 *
094 * The following examples help explain the rules above:
095 *
096 * 1) A message (S1(Original Content)) (where S = SignedData) is sent to
097 *    the MLA in which the signedData layer does not include an
098 *    MLExpansionHistory attribute. The MLA verifies and fully processes
099 *    the signedAttributes in S1.  The MLA decides that there is not an
100 *    original, received "outer" signedData layer since it finds the
101 *    original content, but never finds an envelopedData and never finds
102 *    an mlExpansionHistory attribute. The MLA calculates a new
103 *    signedData layer, S2, resulting in the following message sent to
104 *    the ML recipients: (S2(S1(Original Content))). The MLA includes an
105 *    mlExpansionHistory attribute in S2.
106 *
107 * 2) A message (S3(S2(S1(Original Content)))) is sent to the MLA in
108 *    which none of the signedData layers includes an MLExpansionHistory
109 *    attribute. The MLA verifies and fully processes the
110 *    signedAttributes in S3, S2 and S1. The MLA decides that there is
111 *    not an original, received "outer" signedData layer since it finds
112 *    the original content, but never finds an envelopedData and never
113 *    finds an mlExpansionHistory attribute. The MLA calculates a new
114 *    signedData layer, S4, resulting in the following
115 *    message sent to the ML recipients:
116 *    (S4(S3(S2(S1(Original Content))))). The MLA includes an
117 *    mlExpansionHistory attribute in S4.
118 *
119 * 3) A message (E1(S1(Original Content))) (where E = envelopedData) is
120 *    sent to the MLA in which S1 does not include an MLExpansionHistory
121 *    attribute.  The MLA decides that there is not an original,
122 *    received "outer" signedData layer since it finds the E1 as the
123 *    outer layer.  The MLA expands the recipientInformation in E1. The
124 *    MLA calculates a new signedData layer, S2, resulting in the
125 *    following message sent to the ML recipients:
126 *    (S2(E1(S1(Original Content)))). The MLA includes an
127 *    mlExpansionHistory attribute in S2.
128 *
129 * 4) A message (S2(E1(S1(Original Content)))) is sent to the MLA in
130 *    which S2 includes an MLExpansionHistory attribute. The MLA verifies
131 *    the signature and fully processes the signedAttributes in S2. The
132 *    MLA finds the mlExpansionHistory attribute in S2, so it decides
133 *    that S2 is the "outer" signedData. The MLA remembers the
134 *    signedAttributes included in S2 for later inclusion in the new
135 *    outer signedData that it applies to the message. The MLA strips off
136 *    S2. The MLA then expands the recipientInformation in E1 (this
137 *    invalidates the signature in S2 which is why it was stripped). The
138 *    nMLA calculates a new signedData layer, S3, resulting in the
139 *    following message sent to the ML recipients: (S3(E1(S1(Original
140 *    Content)))). The MLA includes in S3 the attributes from S2 (unless
141 *    it specifically replaces an attribute value) including an updated
142 *    mlExpansionHistory attribute.
143 *
144 * 5) A message (S3(S2(E1(S1(Original Content))))) is sent to the MLA in
145 *    which none of the signedData layers include an MLExpansionHistory
146 *    attribute. The MLA verifies the signature and fully processes the
147 *    signedAttributes in S3 and S2. When the MLA encounters E1, then it
148 *    decides that S2 is the "outer" signedData since S2 encapsulates E1.
149 *    The MLA remembers the signedAttributes included in S2 for later
150 *    inclusion in the new outer signedData that it applies to the
151 *    message.  The MLA strips off S3 and S2. The MLA then expands the
152 *    recipientInformation in E1 (this invalidates the signatures in S3
153 *    and S2 which is why they were stripped). The MLA calculates a new
154 *    signedData layer, S4, resulting in the following message sent to
155 *    the ML recipients: (S4(E1(S1(Original Content)))). The MLA
156 *    includes in S4 the attributes from S2 (unless it specifically
157 *    replaces an attribute value) and includes a new
158 *    mlExpansionHistory attribute.
159 *
160 * 6) A message (S3(S2(E1(S1(Original Content))))) is sent to the MLA in
161 *    which S3 includes an MLExpansionHistory attribute. In this case,
162 *    the MLA verifies the signature and fully processes the
163 *    signedAttributes in S3. The MLA finds the mlExpansionHistory in S3,
164 *    so it decides that S3 is the "outer" signedData. The MLA remembers
165 *    the signedAttributes included in S3 for later inclusion in the new
166 *    outer signedData that it applies to the message. The MLA keeps on
167 *    parsing encapsulated layers because it must determine if there are
168 *    any eSSSecurityLabel attributes contained within. The MLA verifies
169 *    the signature and fully processes the signedAttributes in S2. When
170 *    the MLA encounters E1, then it strips off S3 and S2. The MLA then
171 *    expands the recipientInformation in E1 (this invalidates the
172 *    signatures in S3 and S2 which is why they were stripped). The MLA
173 *    calculates a new signedData layer, S4, resulting in the following
174 *    message sent to the ML recipients: (S4(E1(S1(Original Content)))).
175 *    The MLA includes in S4 the attributes from S3 (unless it
176 *    specifically replaces an attribute value) including an updated
177 *    mlExpansionHistory attribute.
178 * </pre>
179 * To run this demo the following packages are required:
180 * <ul>
181 *    <li>
182 *       <code>iaik_cms.jar</code>
183 *    </li>
184 *    <li>
185 *       <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>).
186 *    </li>
187 *    <li>
188 *       <a href="https://jakarta.ee/specifications/mail/" target="_blank">Jakarta</a>/<a href="https://eclipse-ee4j.github.io/angus-mail/" target="_blank">Angus</a> Mail
189 *    </li>   
190 *    <li>
191 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
192 *    </li> 
193 * </ul>
194 * 
195 * @see iaik.smime.ess.MLExpansionHistory
196 * @see iaik.smime.ess.MLData
197 * @see iaik.smime.ess.MLReceiptPolicy
198 * @see iaik.smime.ess.utils.MLA
199 */
200public class MLADemo {
201  // whether to print dump all generates test messages to System.out
202  final static boolean DUMP_MESSAGES = false;
203  
204  // the first party (that sends a message to the MLA)
205  String senderName_ = "IAIK Demo Sender";
206  // the second party (MLA)
207  String mlaName_ = "IAIK Demo ML Agent";
208  // the third final party (that receives a message from the MLA)
209  String recipientName_ = "IAIK Demo Recipient";
210  // we use the same email address for all parties
211  String senderAddress_ = "\""+senderName_+"\" <smimetest@iaik.at>";
212  String mlaAddress_ = "\""+mlaName_+"\" <smimetest@iaik.at>";
213  String recipientAddress_ = "\""+recipientName_+"\" <smimetest@iaik.at>";
214  String host_ = "mailhost";                       // name of the mailhost
215  
216  // required certificate; read from the demo keystore
217  X509Certificate[] signerCertificatesOfS1_;            // signer certificates of entity S1
218  PrivateKey signerPrivateKeyOfS1_;                     // signer private key of entity S1
219  X509Certificate[] signerCertificatesOfS2_;            // signer certificates of entity S2
220  PrivateKey signerPrivateKeyOfS2_;                     // signer private key of entity S2
221  X509Certificate[] signerCertificatesOfS3_;            // signer certificates of entity S3
222  PrivateKey signerPrivateKeyOfS3_;                     // signer private key of entity S3
223  X509Certificate[] signerCertificatesOfMLA_;           // signer certificates of MLA
224  PrivateKey signerPrivateKeyOfMLA_;                    // signer private key of MLA
225  X509Certificate[] encryptionCertificatesOfMLA_;       // encryption certificates of MLA
226  PrivateKey encryptionPrivateKeyOfMLA_;                // encryption private key of MLA
227  X509Certificate[] certificatesOfMLA_;                 // all (signer + encryption) certificates of the MLA
228  X509Certificate[] encryptionCertificatesOfE1_;        // encryption certificates of entity E1
229  PrivateKey encryptionPrivateKeyOfE1_;                 // encryption private key of entity E1
230  X509Certificate[] recipientCertificates_;             // certificates of the final recipient if the MLA encrypts again
231  PrivateKey recipientPrivateKey_;                      // private key of the final recipient if the MLA encrypts again
232  
233  
234  // keystore data base of the MLA
235  KeyStoreDatabase keyStoreDatabase_;
236  // MLA 
237  MLA mla_;
238  // MLA id
239  EntityIdentifier mlaID_;
240  
241  /**
242   * Empty default constructor. Reads all required keys and certificates
243   * from the demo keystore (created by running @link demo.keystore.SetupCMSKeySrore)
244   * stored at "cms.keystore" in your current working directoy. Inits the ML agent.
245   */ 
246  public MLADemo() {
247    
248    System.out.println();
249    System.out.println("******************************************************************************************");
250    System.out.println("*                                      MLADemo                                           *");
251    System.out.println("*    (shows the usage of the IAIK-CMS MLA utility for running an ESS mail list agent)    *");
252    System.out.println("******************************************************************************************");
253    System.out.println();
254    
255    try {
256      keyStoreDatabase_ = new KeyStoreDatabase();
257      // get the certificates from the KeyStore
258      signerCertificatesOfS1_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
259      signerPrivateKeyOfS1_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_2048_SIGN_1);
260      signerCertificatesOfS2_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
261      signerPrivateKeyOfS2_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
262      signerCertificatesOfS3_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
263      signerPrivateKeyOfS3_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
264      signerCertificatesOfMLA_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_3);
265      signerPrivateKeyOfMLA_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_3);
266      encryptionCertificatesOfMLA_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
267      encryptionPrivateKeyOfMLA_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
268      encryptionCertificatesOfE1_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
269      encryptionPrivateKeyOfE1_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
270      recipientCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_3);
271      recipientPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_3);  
272    
273      // add the certificates and keys of the MLA to its key data base
274      keyStoreDatabase_.addKey(signerPrivateKeyOfMLA_, signerCertificatesOfMLA_, CMSKeyStoreConstants.RSA_2048_SIGN_1);
275      keyStoreDatabase_.addKey(encryptionPrivateKeyOfMLA_, encryptionCertificatesOfMLA_, CMSKeyStoreConstants.RSA_2048_CRYPT_1);
276    
277      // init MLA 
278      mlaID_ = new EntityIdentifier(new IssuerAndSerialNumber(signerCertificatesOfMLA_[0]));
279      mla_ = new MLA(mlaID_);
280      mla_.setDebugStream("MLA", System.out);
281      // to whom the MLA wants to send an encrypted message
282      mla_.setEncryptionInfo(null, 
283                             new RecipientInfo[] { new KeyTransRecipientInfo(recipientCertificates_[0], (AlgorithmID)AlgorithmID.rsaEncryption.clone()) },
284                             (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 
285                             256);
286      mla_.setDebugStream("MLA", System.out);
287      mla_.setKeyDatabase(keyStoreDatabase_);
288      // do not continue to resolve a message if there is an invalid signature in a signed layer
289      mla_.setStopOnInvalidSignature(true);                           
290      System.out.println("MLA signing cert is: " + signerCertificatesOfMLA_[0].getSubjectDN());
291      System.out.println("MLA entity identifier is:\n" + mlaID_ + "\n");
292    } catch (Exception ex) {  
293      ex.printStackTrace();
294          throw new RuntimeException(ex.toString());
295        } 
296  
297  }
298
299  /**
300   * Runs the demo samples.
301   */
302  public void start() {
303    
304    try {
305      
306      // get the default Session
307          Session session = DemoSMimeUtil.getSession();
308
309          // Create a demo Multipart
310      MimeBodyPart mbp1 = new SMimeBodyPart();
311          mbp1.setText("This is a test of the IAIK-CMS S/MIME ESS MLA implementation.\n");
312          // try to test an attachment
313      MimeBodyPart attachment = new SMimeBodyPart();
314      attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
315      attachment.setFileName("test.html");
316      Multipart mp = new SMimeMultipart();
317      mp.addBodyPart(mbp1);
318      mp.addBodyPart(attachment);
319    
320      // whether to create implcit (content included) or explicit signed messages
321      boolean implicit = true;  
322      System.out.println("Implicit demos");
323      
324      // keep original datasource for comparison 
325      MimeMessage tmpMsg = new MimeMessage(session);
326      tmpMsg.setContent(mp);
327      tmpMsg.saveChanges();
328      byte[] dsBytes = getDataSource(tmpMsg.getDataHandler());
329
330      System.out.println("Testing sample 4.2.1,1) from RFC 2634: S1(O) ==> S2(S1(O))");
331      test_S1_O(session, mp, dsBytes, implicit);  
332           
333      System.out.println("Testing sample 4.2.1,2) from RFC 2634: S3(S2(S1(O))) ==> S4(S3(S2(S1(O))))");
334      test_S3_S2_S1_O(session, mp, dsBytes, implicit);  
335
336      System.out.println("Testing sample 4.2.1,3) from RFC 2634: E1(S1(O)) ==> S2(E1(S1(O)))");
337      test_E1_S1_O(session, mp, dsBytes, implicit);  
338
339      System.out.println("Testing sample 4.2.1,4) from RFC 2634: S2(E1(S1(O))) ==> S3(E1(S1(O)))");
340      test_S2_E1_S1_O(session, mp, dsBytes, implicit);
341
342      System.out.println("Testing sample 4.2.1,5) from RFC 2634: S3(S2(E1(S1(O)))) ==> S4(E1(S1(O)))");
343      test_S3_S2_E1_S1_O(session, mp, dsBytes, implicit, false);
344
345      System.out.println("Testing sample 4.2.1,6) from RFC 2634: S3(S2(E1(S1(O)))) ==> S4(E1(S1(O)))");
346      test_S3_S2_E1_S1_O(session, mp, dsBytes, implicit, true);
347      
348      implicit = false;  
349      System.out.println("Explicit demos");
350            
351      System.out.println("Testing sample 4.2.1,1) from RFC 2634: S1(O) ==> S2(S1(O))");
352      test_S1_O(session, mp, dsBytes, implicit);  
353           
354      System.out.println("Testing sample 4.2.1,2) from RFC 2634: S3(S2(S1(O))) ==> S4(S3(S2(S1(O))))");
355      test_S3_S2_S1_O(session, mp, dsBytes, implicit);  
356
357      System.out.println("Testing sample 4.2.1,3) from RFC 2634: E1(S1(O)) ==> S2(E1(S1(O)))");
358      test_E1_S1_O(session, mp, dsBytes, implicit);  
359
360      System.out.println("Testing sample 4.2.1,4) from RFC 2634: S2(E1(S1(O))) ==> S3(E1(S1(O)))");
361      test_S2_E1_S1_O(session, mp, dsBytes, implicit);
362
363      System.out.println("Testing sample 4.2.1,5) from RFC 2634: S3(S2(E1(S1(O)))) ==> S4(E1(S1(O)))");
364      test_S3_S2_E1_S1_O(session, mp, dsBytes, implicit, false);
365
366      System.out.println("Testing sample 4.2.1,6) from RFC 2634: S3(S2(E1(S1(O)))) ==> S4(E1(S1(O)))");
367      test_S3_S2_E1_S1_O(session, mp, dsBytes, implicit, true);
368
369      System.out.println("Ready!");
370      
371    } catch (Exception ex) {  
372      ex.printStackTrace();
373          throw new RuntimeException(ex.toString());
374        }  
375  }
376    
377  /**
378   * Tests the MLA behaviour for a simple signed message according to sample 4.2.1,1) of
379   * <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>:
380   * <pre>
381   * A message (S1(Original Content)) (where S = SignedData) is sent to
382   * the MLA in which the signedData layer does not include an
383   * MLExpansionHistory attribute. The MLA verifies and fully processes
384   * the signedAttributes in S1.  The MLA decides that there is not an
385   * original, received "outer" signedData layer since it finds the
386   * original content, but never finds an envelopedData and never finds
387   * an mlExpansionHistory attribute. The MLA calculates a new
388   * signedData layer, S2, resulting in the following message sent to
389   * the ML recipients: (S2(S1(Original Content))). The MLA includes an
390   * mlExpansionHistory attribute in S2.
391   * </pre>
392   * 
393   * @param session the current mail session
394   * @param mp the multipart content
395   * @param dsBytes the original content dataSorce bytes for comparison
396   * @param implicit whether implicit (content included) or explicit signing shall be used
397   *
398   * @throws Exception if an error coours
399   */
400  public void test_S1_O(Session session, Multipart mp, byte[] dsBytes, boolean implicit) throws Exception {
401    //  RFC2634, 4.2.1, 1) S1(Original Content) ==> MLA(S1(Original Content)
402    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
403    ByteArrayInputStream bais;  // we read from a stream
404    
405    // 1. send signed message to MLA
406    System.out.println("1. Creating signed message and send it to MLA...");
407    Message msg = createMessage(session, senderAddress_, mlaAddress_);
408    msg.setSubject("RFC2634, 4.2.1, 1) S1(Original Content)");
409    SignedContent sc = create_S1_O(mp, 
410                                   mp.getContentType(), 
411                                   implicit,
412                                   signerCertificatesOfS1_,
413                                   signerPrivateKeyOfS1_,
414                                   (AlgorithmID)AlgorithmID.sha256.clone(),
415                                   (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
416                                   null); 
417    msg.setContent(sc, sc.getContentType());                                
418    sc.setHeaders(msg);
419    msg.saveChanges();
420    baos.reset();
421    msg.writeTo(baos);
422    // 2. MLA: receives messages, processes it and creates and sends a new signed message
423    System.out.println("2. MLA: Processing message...");
424    bais = new ByteArrayInputStream(baos.toByteArray());
425    msg = new MimeMessage(session, bais);
426    if (DUMP_MESSAGES) {
427      System.out.println("Received message:");  
428      dumpMessage(msg);
429    }  
430    sc = processMessageForMLA(msg, implicit, "S1_O");
431    System.out.println("MLA: Sending signed message to new recipient.");
432    msg = createMessage(session, mlaAddress_, recipientAddress_);
433    msg.setSubject("RFC2634, 4.2.1, 1) MLA(S1(Original Content)");
434    msg.setContent(sc, sc.getContentType());
435    sc.setHeaders(msg);
436    baos.reset();
437    msg.writeTo(baos);
438    // 3. final recipient: receives message from MLA, parses it and verifies the signature
439    System.out.println("3. Final recipient: Parsing message received from MLA...");
440    bais = new ByteArrayInputStream(baos.toByteArray());
441    msg = new MimeMessage(session, bais);
442    if (DUMP_MESSAGES) {
443      dumpMessage(msg);
444    }    
445    // message must be signed by MLA: S2(S1(O)), checking signature S2
446    Object content = msg.getContent();
447    if ((content instanceof SignedContent == false)) {
448      throw new ESSException("Error: received message is not signed!");
449    }  
450    System.out.println("First layer of message is signed. Verifying signature.");
451    DataHandler dh = verify((SignedContent)content, signerCertificatesOfMLA_[0]);
452    // read MLExpansionHistory attribute, must contain one MLData entry
453    System.out.println("Reading MLExpansionHistory attribute.");
454    readMLExpansionHistory((SignedContent)content, 1);
455    // second layer must be signed, too
456    content = dh.getContent();
457    if ((content instanceof SignedContent == false)) {
458      throw new ESSException("Error: second layer of received message is not signed!");
459    }  
460    System.out.println("Second layer of message is signed. Verifying signature.");
461    dh = verify((SignedContent)content, signerCertificatesOfS1_[0]);
462    // check content
463    if (CryptoUtils.equalsBlock(dsBytes, getDataSource(dh)) == false) {
464      throw new Exception("Error: Original content changed!"); 
465    }  
466    dumpContent(dh);
467  }  
468  
469  
470  /**
471   * Tests the MLA behaviour for a triple signed message according to sample 4.2.1,2) of
472   * <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>:
473   * <pre>
474   * A message (S3(S2(S1(Original Content)))) is sent to the MLA in
475   * which none of the signedData layers includes an MLExpansionHistory
476   * attribute. The MLA verifies and fully processes the
477   * signedAttributes in S3, S2 and S1. The MLA decides that there is
478   * not an original, received "outer" signedData layer since it finds
479   * the original content, but never finds an envelopedData and never
480   * finds an mlExpansionHistory attribute. The MLA calculates a new
481   * signedData layer, S4, resulting in the following message sent to
482   * the ML recipients: (S4(S3(S2(S1(Original Content))))). The MLA 
483   * includes an mlExpansionHistory attribute in S4.
484   * </pre>
485   * 
486   * @param session the current mail session
487   * @param mp the multipart content
488   * @param dsBytes the original content dataSorce bytes for comparison
489   * @param implicit whether implicit (content included) or explicit signing shall be used
490   *
491   * @throws Exception if an error coours
492   */
493  public void test_S3_S2_S1_O(Session session, Multipart mp, byte[] dsBytes, boolean implicit) throws Exception {
494    //  RFC2634, 4.2.1, 2) S3(S2(S1(Original Content))) ==> MLA(S3(S2(S1(Original Content))))
495    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
496    ByteArrayInputStream bais;  // we read from a stream
497    
498    // 1. send signed message to MLA
499    System.out.println("1. Creating signed message and send it to MLA...");
500    Message msg = createMessage(session, senderAddress_, mlaAddress_);
501    msg.setSubject("RFC2634, 4.2.1, 2) S3(S2(S1(Original Content)))");
502    SignedContent sc = create_S3_S2_S1_O(mp, 
503                                         mp.getContentType(), 
504                                         implicit,
505                                         signerCertificatesOfS1_,
506                                         signerPrivateKeyOfS1_,
507                                         (AlgorithmID)AlgorithmID.sha256.clone(),
508                                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
509                                         null,
510                                         implicit,
511                                         signerCertificatesOfS2_,
512                                         signerPrivateKeyOfS2_,
513                                         (AlgorithmID)AlgorithmID.sha256.clone(),
514                                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
515                                         null,
516                                         implicit,
517                                         signerCertificatesOfS3_,
518                                         signerPrivateKeyOfS3_,
519                                         (AlgorithmID)AlgorithmID.sha256.clone(),
520                                         (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
521                                         null); 
522    msg.setContent(sc, sc.getContentType());                                
523    sc.setHeaders(msg);
524    msg.saveChanges();
525    baos.reset();
526    msg.writeTo(baos);
527    // 2. MLA: receives messages, processes it and creates and sends a new signed message
528    System.out.println("2. MLA: Processing message...");
529    bais = new ByteArrayInputStream(baos.toByteArray());
530    msg = new MimeMessage(session, bais);
531    if (DUMP_MESSAGES) {
532      System.out.println("Received message:");  
533      dumpMessage(msg);
534    }  
535    sc = processMessageForMLA(msg, implicit, "S3_S2_S1_O");
536    System.out.println("MLA: Sending signed message to new recipient.");
537    msg = createMessage(session, mlaAddress_, recipientAddress_);
538    msg.setSubject("RFC2634, 4.2.1, 2) MLA(S3(S2(S1(Original Content))))");
539    msg.setContent(sc, sc.getContentType());
540    sc.setHeaders(msg);
541    baos.reset();
542    msg.writeTo(baos);
543    // 3. final recipient: receives message from MLA, parses it and verifies the signature
544    System.out.println("3. Final recipient: Parsing message received from MLA...");
545    bais = new ByteArrayInputStream(baos.toByteArray());
546    msg = new MimeMessage(session, bais);
547    if (DUMP_MESSAGES) {
548      dumpMessage(msg);
549    }    
550    // message must be signed by MLA: S4(S3(S2(S1(O)))), checking signature S2
551    Object content = msg.getContent();
552    if ((content instanceof SignedContent == false)) {
553      throw new ESSException("Error: received message is not signed!");
554    }  
555    System.out.println("First layer of message is signed. Verifying signature.");
556    DataHandler dh = verify((SignedContent)content, signerCertificatesOfMLA_[0]);
557    // read MLExpansionHistory attribute, must contain one MLData entry
558    System.out.println("Reading MLExpansionHistory attribute.");
559    readMLExpansionHistory((SignedContent)content, 1);
560    // second layer must be signed, too
561    content = dh.getContent();
562    if ((content instanceof SignedContent == false)) {
563      throw new ESSException("Error: second layer of received message is not signed!");
564    }  
565    System.out.println("Second layer of message is signed. Verifying signature.");
566    dh = verify((SignedContent)content, signerCertificatesOfS3_[0]);
567    // third layer must be signed, too
568    content = dh.getContent();
569    if ((content instanceof SignedContent == false)) {
570      throw new ESSException("Error: third layer of received message is not signed!");
571    }  
572    System.out.println("Third layer of message is signed. Verifying signature.");
573    dh = verify((SignedContent)content, signerCertificatesOfS2_[0]);
574    // fourth layer must be signed, too
575    content = dh.getContent();
576    if ((content instanceof SignedContent == false)) {
577      throw new ESSException("Error: fourth layer of received message is not signed!");
578    }  
579    System.out.println("Fourth layer of message is signed. Verifying signature.");
580    dh = verify((SignedContent)content, signerCertificatesOfS1_[0]);
581    // check content
582    if (CryptoUtils.equalsBlock(dsBytes, getDataSource(dh)) == false) {
583      throw new Exception("Error: Original content changed!"); 
584    }  
585    dumpContent(dh);
586  }
587  
588  /**
589   * Tests the MLA behaviour for a encrypted and signed signed message according to 
590   * sample 4.2.1,3) of <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>:
591   * <pre>
592   * A message (E1(S1(Original Content))) (where E = envelopedData) is
593   * sent to the MLA in which S1 does not include an MLExpansionHistory
594   * attribute.  The MLA decides that there is not an original,
595   * received "outer" signedData layer since it finds the E1 as the
596   * outer layer.  The MLA expands the recipientInformation in E1. The
597   * MLA calculates a new signedData layer, S2, resulting in the
598   * following message sent to the ML recipients:
599   * (S2(E1(S1(Original Content)))). The MLA includes an
600   * mlExpansionHistory attribute in S2.
601   * </pre>
602   * 
603   * @param session the current mail session
604   * @param mp the multipart content
605   * @param dsBytes the original content dataSorce bytes for comparison
606   * @param implicit whether implicit (content included) or explicit signing shall be used
607   *
608   * @throws Exception if an error coours
609   */
610  public void test_E1_S1_O(Session session, Multipart mp, byte[] dsBytes, boolean implicit) throws Exception {
611    //  RFC2634, 4.2.1, 3) E1(S1(Original Content)) ==> MLA(E1(S1(Original Content)))
612    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
613    ByteArrayInputStream bais;  // we read from a stream
614    
615    // 1. send signed and encrypted message to MLA
616    System.out.println("1. Creating signed and encrypted message and send it to MLA...");
617    Message msg = createMessage(session, senderAddress_, mlaAddress_);
618    msg.setSubject("RFC2634, 4.2.1, 3) E1(S1(Original Content))");
619    EncryptedContent ec = create_E1_S1_O(mp, 
620                                      mp.getContentType(), 
621                                      implicit,
622                                      signerCertificatesOfS1_,
623                                      signerPrivateKeyOfS1_,
624                                      (AlgorithmID)AlgorithmID.sha256.clone(),
625                                      (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
626                                      null,
627                                      encryptionCertificatesOfMLA_[0],
628                                      (AlgorithmID)AlgorithmID.rsaEncryption,
629                                      (AlgorithmID)AlgorithmID.aes128_CBC.clone(),
630                                       128); 
631    msg.setContent(ec, ec.getContentType());                                
632    ec.setHeaders(msg);
633    msg.saveChanges();
634    baos.reset();
635    msg.writeTo(baos);
636    // 2. MLA: receives messages, processes it and creates and sends a new signed message
637    System.out.println("2. MLA: Processing message...");
638    bais = new ByteArrayInputStream(baos.toByteArray());
639    msg = new MimeMessage(session, bais);
640    if (DUMP_MESSAGES) {
641      System.out.println("Received message:");  
642      dumpMessage(msg);
643    }  
644    SignedContent sc = processMessageForMLA(msg, implicit, "E1_S1_O");
645    System.out.println("MLA: Sending signed message to new recipient.");
646    msg = createMessage(session, mlaAddress_, recipientAddress_);
647    msg.setSubject("RFC2634, 4.2.1, 3) MLA(E1(S1(Original Content)))");
648    msg.setContent(sc, sc.getContentType());
649    sc.setHeaders(msg);
650    baos.reset();
651    msg.writeTo(baos);
652    // 3. final recipient: receives message from MLA, parses it
653    System.out.println("3. Final recipient: Parsing message received from MLA...");
654    bais = new ByteArrayInputStream(baos.toByteArray());
655    msg = new MimeMessage(session, bais);
656    if (DUMP_MESSAGES) {
657      dumpMessage(msg);
658    }    
659    // message must be signed by MLA: S2(E1(S1(O))), checking signature S2
660    Object content = msg.getContent();
661    if ((content instanceof SignedContent == false)) {
662      throw new ESSException("Error: received message is not signed!");
663    }  
664    System.out.println("First layer of message is signed. Verifying signature.");
665    DataHandler dh = verify((SignedContent)content, signerCertificatesOfMLA_[0]);
666    // read MLExpansionHistory attribute, must contain one MLData entry
667    System.out.println("Reading MLExpansionHistory attribute.");
668    readMLExpansionHistory((SignedContent)content, 1);
669    // second layer must be encrypted
670    content = dh.getContent();
671    if ((content instanceof EncryptedContent == false)) {
672      throw new ESSException("Error: second layer of received message is not encrypted!");
673    }  
674    System.out.println("Second layer of message is encrypted. Trying to decrypt.");
675    dh = decrypt((EncryptedContent)content, recipientPrivateKey_, recipientCertificates_[0]);
676    // third layer must be signed, too
677    content = dh.getContent();
678    if ((content instanceof SignedContent == false)) {
679      throw new ESSException("Error: second layer of received message is not signed!");
680    }  
681    System.out.println("Third layer of message is signed. Verifying signature.");
682    dh = verify((SignedContent)content, signerCertificatesOfS1_[0]);
683    // check content
684    if (CryptoUtils.equalsBlock(dsBytes, getDataSource(dh)) == false) {
685      throw new Exception("Error: Original content changed!"); 
686    }  
687    dumpContent(dh);
688  }
689  
690  /**
691   * Tests the MLA behaviour for signed encrypted and signed signed message according to 
692   * sample 4.2.1,4) of <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>:
693   * <pre>
694   * A message (S2(E1(S1(Original Content)))) is sent to the MLA in
695   * which S2 includes an MLExpansionHistory attribute. The MLA verifies
696   * the signature and fully processes the signedAttributes in S2. The
697   * MLA finds the mlExpansionHistory attribute in S2, so it decides
698   * that S2 is the "outer" signedData. The MLA remembers the
699   * signedAttributes included in S2 for later inclusion in the new
700   * outer signedData that it applies to the message. The MLA strips off
701   * S2. The MLA then expands the recipientInformation in E1 (this
702   * invalidates the signature in S2 which is why it was stripped). The
703   * nMLA calculates a new signedData layer, S3, resulting in the
704   * following message sent to the ML recipients: (S3(E1(S1(Original
705   * Content)))). The MLA includes in S3 the attributes from S2 (unless
706   * it specifically replaces an attribute value) including an updated
707   * mlExpansionHistory attribute.
708   * </pre>
709   * 
710   * @param session the current mail session
711   * @param mp the multipart content
712   * @param dsBytes the original content dataSorce bytes for comparison
713   * @param implicit whether implicit (content included) or explicit signing shall be used
714   *
715   * @throws Exception if an error coours
716   */
717  public void test_S2_E1_S1_O(Session session, Multipart mp, byte[] dsBytes, boolean implicit) throws Exception {
718    //  RFC2634, 4.2.1, 4) S2(E1(S1(Original Content))) ==> MLA(E1(S1(Original Content)))
719    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
720    ByteArrayInputStream bais;  // we read from a stream
721    
722    // 1. send signed and encrypted and signed message to MLA
723    System.out.println("1. Creating signed and encrypted/signed message and send it to MLA...");
724    Message msg = createMessage(session, senderAddress_, mlaAddress_);
725    msg.setSubject("RFC2634, 4.2.1, 4) S2(E1(S1(Original Content)))");
726    SignedContent sc = create_S2_E1_S1_0(mp, 
727                                         mp.getContentType(), 
728                                         implicit,
729                                         signerCertificatesOfS1_,
730                                         signerPrivateKeyOfS1_,
731                                         (AlgorithmID)AlgorithmID.sha256.clone(),
732                                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
733                                         null,
734                                         encryptionCertificatesOfMLA_[0],
735                                         (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
736                                         (AlgorithmID)AlgorithmID.aes128_CBC.clone(),
737                                         128,
738                                         implicit,
739                                         signerCertificatesOfS2_,
740                                         signerPrivateKeyOfS2_,
741                                         (AlgorithmID)AlgorithmID.sha256.clone(),
742                                         (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
743                                         createMLExpansionHistory(signerCertificatesOfS2_[0], new Date(), null)); 
744    msg.setContent(sc, sc.getContentType());                                
745    sc.setHeaders(msg);
746    msg.saveChanges();
747    baos.reset();
748    msg.writeTo(baos);
749    // 2. MLA: receives messages, processes it and creates and sends a new signed message
750    System.out.println("2. MLA: Processing message...");
751    bais = new ByteArrayInputStream(baos.toByteArray());
752    msg = new MimeMessage(session, bais);
753    if (DUMP_MESSAGES) {
754      System.out.println("Received message:");  
755      dumpMessage(msg);
756    }  
757    sc = processMessageForMLA(msg, implicit, "S2_E1_S1_0");
758    System.out.println("MLA: Sending signed message to new recipient.");
759    msg = createMessage(session, mlaAddress_, recipientAddress_);
760    msg.setSubject("RFC2634, 4.2.1, 4) MLA(E1(S1(Original Content)))");
761    msg.setContent(sc, sc.getContentType());
762    sc.setHeaders(msg);
763    baos.reset();
764    msg.writeTo(baos);
765    // 3. final recipient: receives message from MLA, parses it
766    System.out.println("3. Final recipient: Parsing message received from MLA...");
767    bais = new ByteArrayInputStream(baos.toByteArray());
768    msg = new MimeMessage(session, bais);
769    if (DUMP_MESSAGES) {
770      dumpMessage(msg);
771    }    
772    // message must be signed by MLA: S3(E1(S1(O))), checking signature S3
773    Object content = msg.getContent();
774    if ((content instanceof SignedContent == false)) {
775      throw new ESSException("Error: received message is not signed!");
776    }  
777    System.out.println("First layer of message is signed. Verifying signature.");
778    DataHandler dh = verify((SignedContent)content, signerCertificatesOfMLA_[0]);
779    // read MLExpansionHistory attribute, must contain one MLData entry
780    System.out.println("Reading MLExpansionHistory attributes.");
781    readMLExpansionHistory((SignedContent)content, 2);
782    // second layer must be encrypted
783    content = dh.getContent();
784    if ((content instanceof EncryptedContent == false)) {
785      throw new ESSException("Error: second layer of received message is not encrypted!");
786    }  
787    System.out.println("Second layer of message is encrypted. Trying to decrypt.");
788    dh = decrypt((EncryptedContent)content, recipientPrivateKey_, recipientCertificates_[0]);
789    // third layer must be signed, too
790    content = dh.getContent();
791    if ((content instanceof SignedContent == false)) {
792      throw new ESSException("Error: second layer of received message is not signed!");
793    }  
794    System.out.println("Third layer of message is signed. Verifying signature.");
795    dh = verify((SignedContent)content, signerCertificatesOfS1_[0]);
796    // check content
797    if (CryptoUtils.equalsBlock(dsBytes, getDataSource(dh)) == false) {
798      throw new Exception("Error: Original content changed!"); 
799    }  
800    dumpContent(dh);
801  }
802  
803  /**
804   * Tests the MLA behaviour for double signed encrypted and signed signed message 
805   * according to  sample 4.2.1,5) of <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a>:
806   * <pre>
807   * A message (S3(S2(E1(S1(Original Content))))) is sent to the MLA in
808   * which none of the signedData layers include an MLExpansionHistory
809   * attribute. The MLA verifies the signature and fully processes the
810   * signedAttributes in S3 and S2. When the MLA encounters E1, then it
811   * decides that S2 is the "outer" signedData since S2 encapsulates E1.
812   * The MLA remembers the signedAttributes included in S2 for later
813   * inclusion in the new outer signedData that it applies to the
814   * message.  The MLA strips off S3 and S2. The MLA then expands the
815   * recipientInformation in E1 (this invalidates the signatures in S3
816   * and S2 which is why they were stripped). The MLA calculates a new
817   * signedData layer, S4, resulting in the following message sent to
818   * the ML recipients: (S4(E1(S1(Original Content)))). The MLA
819   * includes in S4 the attributes from S2 (unless it specifically
820   * replaces an attribute value) and includes a new
821   * mlExpansionHistory attribute.
822   * </pre>
823   * 
824   * @param session the current mail session
825   * @param mp the multipart content
826   * @param dsBytes the original content dataSorce bytes for comparison
827   * @param implicit whether implicit (content included) or explicit signing shall be used
828   * @param includeMLExpansionHistoryInS3 whether to include an MLExpansionHistory in the
829   *                                      the outermost signed layer (S3) of the original
830   *                                      message
831   *
832   * @throws Exception if an error coours
833   */
834  public void test_S3_S2_E1_S1_O(Session session, 
835                                 Multipart mp, 
836                                 byte[] dsBytes, 
837                                 boolean implicit,
838                                 boolean includeMLExpansionHistoryInS3) throws Exception {
839    //  RFC2634, 4.2.1, 5) S3(S2(E1(S1(Original Content)))) ==> MLA(E1(S1(Original Content)))
840    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
841    ByteArrayInputStream bais;  // we read from a stream
842    
843    // 1. send double signed and encrypted and signed message to MLA
844    System.out.println("1. Creating double signed and encrypted/signed message and send it to MLA...");
845    Message msg = createMessage(session, senderAddress_, mlaAddress_);
846    msg.setSubject("RFC2634, 4.2.1, 5) S3(S2(E1(S1(Original Content))))");
847    SignedContent sc = create_S3_S2_E1_S1_0(mp, 
848                                            mp.getContentType(), 
849                                            implicit,
850                                            signerCertificatesOfS1_,
851                                            signerPrivateKeyOfS1_,
852                                            (AlgorithmID)AlgorithmID.sha256.clone(),
853                                            (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
854                                            null,
855                                            encryptionCertificatesOfMLA_[0],
856                                            (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
857                                            (AlgorithmID)AlgorithmID.aes128_CBC.clone(),
858                                            128,
859                                            implicit,
860                                            signerCertificatesOfS2_,
861                                            signerPrivateKeyOfS2_,
862                                            (AlgorithmID)AlgorithmID.sha256.clone(),
863                                            (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
864                                            null,
865                                            implicit,
866                                            signerCertificatesOfS3_,
867                                            signerPrivateKeyOfS3_,
868                                            (AlgorithmID)AlgorithmID.sha256.clone(),
869                                            (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
870                                            (includeMLExpansionHistoryInS3 ?
871                                            createMLExpansionHistory(signerCertificatesOfS3_[0], new Date(), null) :
872                                            null));
873    msg.setContent(sc, sc.getContentType());                                
874    sc.setHeaders(msg);
875    msg.saveChanges();
876    baos.reset();
877    msg.writeTo(baos);
878    // 2. MLA: receives messages, processes it and creates and sends a new signed message
879    System.out.println("2. MLA: Processing message...");
880    bais = new ByteArrayInputStream(baos.toByteArray());
881    msg = new MimeMessage(session, bais);
882    if (DUMP_MESSAGES) {
883      System.out.println("Received message:");  
884      dumpMessage(msg);
885    }  
886    sc = processMessageForMLA(msg, implicit, "S3_S2_E1_S1_0");
887    System.out.println("MLA: Sending signed message to new recipient.");
888    msg = createMessage(session, mlaAddress_, recipientAddress_);
889    msg.setSubject("RFC2634, 4.2.1, 5) MLA(E1(S1(Original Content)))");
890    msg.setContent(sc, sc.getContentType());
891    sc.setHeaders(msg);
892    baos.reset();
893    msg.writeTo(baos);
894    // 3. final recipient: receives message from MLA, parses it
895    System.out.println("3. Final recipient: Parsing message received from MLA...");
896    bais = new ByteArrayInputStream(baos.toByteArray());
897    msg = new MimeMessage(session, bais);
898    if (DUMP_MESSAGES) {
899      dumpMessage(msg);
900    }    
901    // message must be signed by MLA: S4(E1(S1(O))), checking signature S4
902    Object content = msg.getContent();
903    if ((content instanceof SignedContent == false)) {
904      throw new ESSException("Error: received message is not signed!");
905    }  
906    System.out.println("First layer of message is signed. Verifying signature.");
907    DataHandler dh = verify((SignedContent)content, signerCertificatesOfMLA_[0]);
908    // read MLExpansionHistory attribute, must contain one MLData entry
909    System.out.println("Reading MLExpansionHistory attributes.");
910    readMLExpansionHistory((SignedContent)content, includeMLExpansionHistoryInS3 ? 2 : 1);
911    // second layer must be encrypted
912    content = dh.getContent();
913    if ((content instanceof EncryptedContent == false)) {
914      throw new ESSException("Error: second layer of received message is not encrypted!");
915    }  
916    System.out.println("Second layer of message is encrypted. Trying to decrypt.");
917    dh = decrypt((EncryptedContent)content, recipientPrivateKey_, recipientCertificates_[0]);
918    // third layer must be signed, too
919    content = dh.getContent();
920    if ((content instanceof SignedContent == false)) {
921      throw new ESSException("Error: second layer of received message is not signed!");
922    }  
923    System.out.println("Third layer of message is signed. Verifying signature.");
924    dh = verify((SignedContent)content, signerCertificatesOfS1_[0]);
925    // check content
926    if (CryptoUtils.equalsBlock(dsBytes, getDataSource(dh)) == false) {
927      throw new Exception("Error: Original content changed!"); 
928    }  
929    dumpContent(dh);
930  }
931
932  /**
933   * Creates a new MimeMessage without content and sets the From:, To:, and Date: headers.
934   * 
935   * @param session the current mail session
936   * @param from the address of the sender of the message
937   * @param to the address of the indented message recipient
938   * @return the new created MimeMessage
939   * @throws MessagingException if an error occurs when setting the message headers
940   */
941  public Message createMessage(Session session, String from, String to) throws MessagingException {
942    MimeMessage msg = new MimeMessage(session);
943    msg.setFrom(new InternetAddress(from));
944        msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to, false));
945        msg.setSentDate(new Date());
946    return msg;
947  }
948  
949  /**
950   * Creates a SignedContent.
951   *
952   * @param content the content to be signed
953   * @param contentType the MIME type of the content
954   * @param implicit whether to create an implicit (application/pkcs7-mime) or 
955   *                 explicit (multipart/signed) message
956   * @param signerCertificates the certificate chain of the signer
957   * @param signerPrivateKey the private key to be used for signing the content
958   * @param digestAlg the algorithm to be used for digest calculation
959   * @param signatureAlg the algorithm to be used for signature calculation
960   * @param mlExpansionHistory MLExpansionHistory attribute to be added; maybe null
961   *
962   * @return the SignedContent
963   *
964   * @throws MessagingException if a problem occurs when creating the SignedContent
965   */
966  public SignedContent createSignedContent(Object content, 
967                                           String contentType, 
968                                           boolean implicit,
969                                           X509Certificate[] signerCertificates,
970                                           PrivateKey signerPrivateKey,
971                                           AlgorithmID digestAlg,
972                                           AlgorithmID signatureAlg,
973                                           MLExpansionHistory mlExpansionHistory)
974    throws MessagingException {
975
976    SignedContent sc = new SignedContent(implicit);
977    sc.setContent(content, contentType);
978    sc.setCertificates(signerCertificates);
979    try {
980      // create a set of standard attributes
981      Attributes attributes = SMimeUtil.makeStandardAttributes();
982      if (mlExpansionHistory != null) {
983        attributes.addAttribute(new Attribute(mlExpansionHistory));
984      }
985      sc.addSigner(signerPrivateKey, signerCertificates[0], digestAlg, signatureAlg, attributes.toArray());
986    } catch (NoSuchAlgorithmException ex) {
987      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
988    } catch (CodingException ex) {
989      throw new MessagingException("Error in attribute encoding: " + ex.getMessage());   
990    } catch (SMimeException ex) {
991      throw new MessagingException("Error adding attributes: " + ex.toString());   
992    }    
993
994    return sc;
995  }
996  
997  /**
998   * Creates an EncryptedContent.
999   *
1000   * @param content the content to be encrypted
1001   * @param contentType the MIME type of the content
1002   * @param recipientCertificate the encryption certificate of the recipient
1003   * @param cekEncrAlg the algorithm to be used for encrypting the symmetric content encryption key
1004   *                   (e.g. AlgorithmID.rsaEncryption)
1005   * @param contentEncrAlg the symmetric key to be used for encrypting the content, e.g. AlgorithmID.aes256_CBC
1006   * @param cekLength the length of the temporary content encryption key to be generated (e.g. 256)
1007   *
1008   * @return the EncryptedContent
1009   *
1010   * @throws MessagingException if a problem occurs when creating the EncryptedContent
1011   */
1012  public EncryptedContent createEncryptedContent(Object content, 
1013                                                 String contentType,
1014                                                 X509Certificate recipientCertificate,
1015                                                 AlgorithmID cekEncrAlg,
1016                                                 AlgorithmID contentEncrAlg,
1017                                                 int cekLength) throws MessagingException {
1018
1019    EncryptedContent ec = new EncryptedContent();
1020    ec.setContent(content, contentType);
1021    // encrypt for the recipient
1022    ec.addRecipient(recipientCertificate, cekEncrAlg);
1023    try {
1024      ec.setEncryptionAlgorithm(contentEncrAlg, cekLength);
1025    } catch (NoSuchAlgorithmException ex) {
1026      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
1027    }   
1028
1029    return ec;
1030  }
1031  
1032  /**
1033   * Signs the given content.
1034   *
1035   * @param content the content to be signed
1036   * @param contentType the MIME type of the content
1037   * @param implicitS1 whether to create an implicit (application/pkcs7-mime) or 
1038   *                 explicit (multipart/signed) message
1039   * @param signerCertificatesS1 the certificate chain of the signer
1040   * @param signerPrivateKeyS1 the private key to be used for signing the content
1041   * @param digestAlgS1 the algorithm to be used for digest calculation
1042   * @param signatureAlgS1 the algorithm to be used for signature calculation
1043   * @param mlExpansionHistoryS1 MLExpansionHistory attribute to be added; maybe null
1044   *
1045   * @return the SignedContent
1046   *
1047   * @throws MessagingException if a problem occurs when creating the SignedContent
1048   */
1049  public SignedContent create_S1_O(Object content, 
1050                                   String contentType, 
1051                                   boolean implicitS1,
1052                                   X509Certificate[] signerCertificatesS1,
1053                                   PrivateKey signerPrivateKeyS1,
1054                                   AlgorithmID digestAlgS1,
1055                                   AlgorithmID signatureAlgS1,
1056                                   MLExpansionHistory mlExpansionHistoryS1) 
1057    throws MessagingException {
1058                                    
1059    return createSignedContent(content, 
1060                               contentType, 
1061                               implicitS1, 
1062                               signerCertificatesS1, 
1063                               signerPrivateKeyS1, 
1064                               digestAlgS1, 
1065                               signatureAlgS1,
1066                               mlExpansionHistoryS1);                                  
1067  }                                    
1068  
1069  /**
1070   * Triple-signs the given content.
1071   *
1072   * @param content the content to be signed
1073   * @param contentType the MIME type of the content
1074   * @param implicitS1 if the first signature shall be implicit (application/pkcs7-mime) or 
1075   *                   explicit (multipart/signed) 
1076   * @param signerCertificatesS1 the certificate chain of the first signer
1077   * @param signerPrivateKeyS1 the private key of the first signer
1078   * @param digestAlgS1 the digest algorithm to be used for digest calculation by the innermost SignedContent
1079   * @param signatureAlgS1 the algorithm to be used for signature calculation by the innermost SignedContent
1080   * @param mlExpansionHistoryS1 MLExpansionHistory attribute to be added to the innermost SignedContent; maybe null
1081   * @param implicitS2 if the second signature shall be implicit (application/pkcs7-mime) or 
1082   *                   explicit (multipart/signed) 
1083   * @param signerCertificatesS2 the certificate chain of the second signer
1084   * @param signerPrivateKeyS2 the private key of the second signer
1085   * @param digestAlgS2 the digest algorithm to be used for digest calculation by the middle SignedContent
1086   * @param signatureAlgS2 the algorithm to be used for signature calculation by the middle SignedContent
1087   * @param mlExpansionHistoryS2 MLExpansionHistory attribute to be added to the middle SignedContent; maybe null
1088   * @param implicitS3 if the first signature shall be implicit (application/pkcs7-mime) or 
1089   *                   explicit (multipart/signed) 
1090   * @param signerCertificatesS3 the certificate chain of the third signer
1091   * @param signerPrivateKeyS3 the private key of the third signer
1092   * @param digestAlgS3 the digest algorithm to be used for digest calculation by the outermost SignedContent
1093   * @param signatureAlgS3 the algorithm to be used for signature calculation by the outermost SignedContent
1094   * @param mlExpansionHistoryS3 MLExpansionHistory attribute to be added for the outermost SignedContent; maybe null
1095   *
1096   * @return the SignedContent
1097   *
1098   * @throws MessagingException if a problem occurs when creating the SignedContent
1099   */
1100  public SignedContent create_S3_S2_S1_O(Object content, 
1101                                         String contentType, 
1102                                         boolean implicitS1,
1103                                         X509Certificate[] signerCertificatesS1,
1104                                         PrivateKey signerPrivateKeyS1,
1105                                         AlgorithmID digestAlgS1,
1106                                         AlgorithmID signatureAlgS1,
1107                                         MLExpansionHistory mlExpansionHistoryS1,
1108                                         boolean implicitS2,
1109                                         X509Certificate[] signerCertificatesS2,
1110                                         PrivateKey signerPrivateKeyS2,
1111                                         AlgorithmID digestAlgS2,
1112                                         AlgorithmID signatureAlgS2,
1113                                         MLExpansionHistory mlExpansionHistoryS2,
1114                                         boolean implicitS3,
1115                                         X509Certificate[] signerCertificatesS3,
1116                                         PrivateKey signerPrivateKeyS3,
1117                                         AlgorithmID digestAlgS3,
1118                                         AlgorithmID signatureAlgS3,
1119                                         MLExpansionHistory mlExpansionHistoryS3) 
1120     throws MessagingException {
1121     SignedContent s1 = create_S1_O(content, contentType, 
1122                                    implicitS1, signerCertificatesS1, signerPrivateKeyS1, digestAlgS1, signatureAlgS1, mlExpansionHistoryS1); 
1123     SignedContent s2 = createSignedContent(s1, s1.getContentType(), 
1124                                            implicitS2, signerCertificatesS2, signerPrivateKeyS2, digestAlgS2, signatureAlgS2, mlExpansionHistoryS2);                            
1125     return createSignedContent(s2, s2.getContentType(), 
1126                                implicitS3, signerCertificatesS3, signerPrivateKeyS3, digestAlgS3, signatureAlgS3, mlExpansionHistoryS3);                                                          
1127  }  
1128  
1129  /**
1130   * Encrypts and signs the given content.
1131   *
1132   * @param content the content to be signed
1133   * @param contentType the MIME type of the content
1134   * @param implicitS1 whether to create an implicit (application/pkcs7-mime) or 
1135   *                 explicit (multipart/signed) message
1136   * @param signerCertificatesS1 the certificate chain of the signer
1137   * @param signerPrivateKeyS1 the private key to be used for signing the content
1138   * @param digestAlgS1 the algorithm to be used for digest calculation
1139   * @param signatureAlgS1 the algorithm to be used for signature calculation
1140   * @param mlExpansionHistoryS1 MLExpansionHistory attribute to be added; maybe null
1141   * @param recipientCertificate the encryption certificate of the recipient
1142   * @param cekEncrAlg the algorithm to be used for encrypting the symmetric content encryption key
1143   *                   (e.g. AlgorithmID.rsaEncryption)
1144   * @param contentEncrAlg the symmetric key to be used for encrypting the content, e.g. AlgorithmID.aes256_CBC
1145   * @param cekLength the length of the temporary content encryption key to be generated (e.g. 256)
1146   *
1147   * @return the signed and encrypted message
1148   *
1149   * @throws MessagingException if a problem occurs when creating the SignedContent or EncryptedContent
1150   */
1151  public EncryptedContent create_E1_S1_O(Object content, 
1152                                         String contentType, 
1153                                         boolean implicitS1,
1154                                         X509Certificate[] signerCertificatesS1,
1155                                         PrivateKey signerPrivateKeyS1,
1156                                         AlgorithmID digestAlgS1,
1157                                         AlgorithmID signatureAlgS1,
1158                                         MLExpansionHistory mlExpansionHistoryS1,
1159                                         X509Certificate recipientCertificate,
1160                                         AlgorithmID cekEncrAlg,
1161                                         AlgorithmID contentEncrAlg,
1162                                         int cekLength) 
1163     throws MessagingException {
1164        
1165     SignedContent s1 = create_S1_O(content, contentType, 
1166                                    implicitS1, signerCertificatesS1, signerPrivateKeyS1, digestAlgS1, signatureAlgS1, mlExpansionHistoryS1); 
1167     return createEncryptedContent(s1, s1.getContentType(), 
1168                                   recipientCertificate, cekEncrAlg, contentEncrAlg, cekLength);                                       
1169  }    
1170  
1171  
1172  /**
1173   * Signs and encrypts and signs the given content.
1174   *
1175   * @param content the content to be signed
1176   * @param contentType the MIME type of the content
1177   * @param implicitS1 if the first signature shall be implicit (application/pkcs7-mime) or 
1178   *                   explicit (multipart/signed) 
1179   * @param signerCertificatesS1 the certificate chain of the first signer
1180   * @param signerPrivateKeyS1 the private key of the first signer
1181   * @param digestAlgS1 the digest algorithm to be used for digest calculation by the innermost SignedContent
1182   * @param signatureAlgS1 the algorithm to be used for signature calculation by the innermost SignedContent
1183   * @param mlExpansionHistoryS1 MLExpansionHistory attribute to be added to the innermost SignedContent; maybe null
1184   * @param recipientCertificate the encryption certificate of the recipient
1185   * @param cekEncrAlg the algorithm to be used for encrypting the symmetric content encryption key
1186   *                   (e.g. AlgorithmID.rsaEncryption)
1187   * @param contentEncrAlg the symmetric key to be used for encrypting the content, e.g. AlgorithmID.aes256_CBC
1188   * @param cekLength the length of the temporary content encryption key to be generated (e.g. 256)
1189   * @param implicitS2 if the second signature shall be implicit (application/pkcs7-mime) or 
1190   *                   explicit (multipart/signed) 
1191   * @param signerCertificatesS2 the certificate chain of the second signer
1192   * @param signerPrivateKeyS2 the private key of the second signer
1193   * @param digestAlgS2 the digest algorithm to be used for digest calculation by the outer SignedContent
1194   * @param signatureAlgS2 the algorithm to be used for signature calculation by the outer SignedContent
1195   * @param mlExpansionHistoryS2 MLExpansionHistory attribute to be added to the outer SignedContent; maybe null
1196   *
1197   * @return the signed and encrypted and signed message
1198   *
1199   * @throws MessagingException if a problem occurs when creating a SignedContent or EncryptedContent
1200   */
1201  public SignedContent create_S2_E1_S1_0(Object content, 
1202                                         String contentType, 
1203                                         boolean implicitS1,
1204                                         X509Certificate[] signerCertificatesS1,
1205                                         PrivateKey signerPrivateKeyS1,
1206                                         AlgorithmID digestAlgS1,
1207                                         AlgorithmID signatureAlgS1,
1208                                         MLExpansionHistory mlExpansionHistoryS1,
1209                                         X509Certificate recipientCertificate,
1210                                         AlgorithmID cekEncrAlg,
1211                                         AlgorithmID contentEncrAlg,
1212                                         int cekLength,
1213                                         boolean implicitS2,
1214                                         X509Certificate[] signerCertificatesS2,
1215                                         PrivateKey signerPrivateKeyS2,
1216                                         AlgorithmID digestAlgS2,
1217                                         AlgorithmID signatureAlgS2,
1218                                         MLExpansionHistory mlExpansionHistoryS2) 
1219    throws MessagingException {
1220        
1221    EncryptedContent e1 = create_E1_S1_O(content, 
1222                                         contentType, 
1223                                         implicitS1,
1224                                         signerCertificatesS1,
1225                                         signerPrivateKeyS1,
1226                                         digestAlgS1,
1227                                         signatureAlgS1,
1228                                         mlExpansionHistoryS1,
1229                                         recipientCertificate,
1230                                         cekEncrAlg,
1231                                         contentEncrAlg,
1232                                         cekLength);
1233    return createSignedContent(e1, e1.getContentType(), 
1234                               implicitS2, signerCertificatesS2, signerPrivateKeyS2, digestAlgS2, signatureAlgS2, mlExpansionHistoryS2);                                     
1235  }      
1236  
1237  /**
1238   * Signs and encrypts and double-signs the given content.
1239   *
1240   * @param content the content to be signed
1241   * @param contentType the MIME type of the content
1242   * @param implicitS1 if the first signature shall be implicit (application/pkcs7-mime) or 
1243   *                   explicit (multipart/signed) 
1244   * @param signerCertificatesS1 the certificate chain of the first signer
1245   * @param signerPrivateKeyS1 the private key of the first signer
1246   * @param digestAlgS1 the digest algorithm to be used for digest calculation by the innermost SignedContent
1247   * @param signatureAlgS1 the algorithm to be used for signature calculation by the innermost SignedContent
1248   * @param mlExpansionHistoryS1 MLExpansionHistory attribute to be added to the innermost SignedContent; maybe null
1249   * @param recipientCertificate the encryption certificate of the recipient
1250   * @param cekEncrAlg the algorithm to be used for encrypting the symmetric content encryption key
1251   *                   (e.g. AlgorithmID.rsaEncryption)
1252   * @param contentEncrAlg the symmetric key to be used for encrypting the content, e.g. AlgorithmID.aes256_CBC
1253   * @param cekLength the length of the temporary content encryption key to be generated (e.g. 256)
1254   * @param implicitS2 if the second signature shall be implicit (application/pkcs7-mime) or 
1255   *                   explicit (multipart/signed) 
1256   * @param signerCertificatesS2 the certificate chain of the second signer
1257   * @param signerPrivateKeyS2 the private key of the second signer
1258   * @param digestAlgS2 the digest algorithm to be used for digest calculation by the middle SignedContent
1259   * @param signatureAlgS2 the algorithm to be used for signature calculation by the middle SignedContent
1260   * @param mlExpansionHistoryS2 MLExpansionHistory attribute to be added to the middle SignedContent; maybe null
1261   * @param implicitS3 if the first signature shall be implicit (application/pkcs7-mime) or 
1262   *                   explicit (multipart/signed) 
1263   * @param signerCertificatesS3 the certificate chain of the third signer
1264   * @param signerPrivateKeyS3 the private key of the third signer
1265   * @param digestAlgS3 the digest algorithm to be used for digest calculation by the outermost SignedContent
1266   * @param signatureAlgS3 the algorithm to be used for signature calculation by the outermost SignedContent
1267   * @param mlExpansionHistoryS3 MLExpansionHistory attribute to be added for the outermost SignedContent; maybe null
1268   *
1269   * @return the signed and encrypted and double-signed message
1270   *
1271   * @throws MessagingException if a problem occurs when creating a SignedContent or EncryptedContent
1272   */
1273  public SignedContent create_S3_S2_E1_S1_0(Object content, 
1274                                           String contentType, 
1275                                           boolean implicitS1,
1276                                           X509Certificate[] signerCertificatesS1,
1277                                           PrivateKey signerPrivateKeyS1,
1278                                           AlgorithmID digestAlgS1,
1279                                           AlgorithmID signatureAlgS1,
1280                                           MLExpansionHistory mlExpansionHistoryS1,
1281                                           X509Certificate recipientCertificate,
1282                                           AlgorithmID cekEncrAlg,
1283                                           AlgorithmID contentEncrAlg,
1284                                           int cekLength,
1285                                           boolean implicitS2,
1286                                           X509Certificate[] signerCertificatesS2,
1287                                           PrivateKey signerPrivateKeyS2,
1288                                           AlgorithmID digestAlgS2,
1289                                           AlgorithmID signatureAlgS2,
1290                                           MLExpansionHistory mlExpansionHistoryS2,
1291                                           boolean implicitS3,
1292                                           X509Certificate[] signerCertificatesS3,
1293                                           PrivateKey signerPrivateKeyS3,
1294                                           AlgorithmID digestAlgS3,
1295                                           AlgorithmID signatureAlgS3,
1296                                           MLExpansionHistory mlExpansionHistoryS3) 
1297    throws MessagingException {
1298        
1299    SignedContent s2 = create_S2_E1_S1_0(content, 
1300                                         contentType, 
1301                                         implicitS1,
1302                                         signerCertificatesS1,
1303                                         signerPrivateKeyS1,
1304                                         digestAlgS1,
1305                                         signatureAlgS1,
1306                                         mlExpansionHistoryS1,
1307                                         recipientCertificate,
1308                                         cekEncrAlg,
1309                                         contentEncrAlg,
1310                                         cekLength,
1311                                         implicitS2,
1312                                         signerCertificatesS2,
1313                                         signerPrivateKeyS2,
1314                                         digestAlgS2,
1315                                         signatureAlgS2,
1316                                         mlExpansionHistoryS2) ;
1317    
1318    return createSignedContent(s2, s2.getContentType(), 
1319                               implicitS3, signerCertificatesS3, signerPrivateKeyS3, digestAlgS3, signatureAlgS3, mlExpansionHistoryS3);                                     
1320  }      
1321  
1322  /**
1323   * Decrypts the encrypted content with the given key of the identified recipient.
1324   * 
1325   * @param ec the EncryptedContent to be decrypted
1326   * @param privateKey the private key to be used to decrypt the encrypted content
1327   * @param certificate the certificate identifying the recipient for which to decrypt the encrypted content
1328   *
1329   * @return the DataHandler holding the recovered (decrypted) content
1330   *
1331   * @throws SMimeException if an error occurs while decrypting the content
1332   */
1333  public DataHandler decrypt(EncryptedContent ec, PrivateKey privateKey, X509Certificate certificate)
1334    throws SMimeException {
1335    try {    
1336      ec.decryptSymmetricKey(privateKey, certificate);
1337      return ec.getDataHandler();
1338    } catch (Exception ex) {
1339      throw new SMimeException(ex.toString());   
1340    }    
1341  }  
1342  
1343  /**
1344   * Verifies the signature of the given SignedContent and returns the inherent content data.
1345   * 
1346   * @param sc the SignedContent to be verified
1347   * @param signerCert the certificate of the signer (to check if the message has been signed
1348   *                                                  by the expected entity)
1349   * @return the inherent content data
1350   * @throws CMSSignatureException if the signature is invalid
1351   * @throws ESSException if an error occurs when accessing the inherent content or
1352   *                         the message has been signed by an unexpected entity
1353   * @throws MessagingException if an error occurs when accessing the content
1354    */
1355  public DataHandler verify(SignedContent sc, X509Certificate signerCert) 
1356    throws CMSSignatureException, MessagingException, ESSException {
1357        
1358    X509Certificate signer = sc.verify(); 
1359    System.out.println("Signature ok from: "+signer.getSubjectDN());
1360    if (signer.equals(signerCert) == false) {
1361      throw new ESSException("Error: message signed by wrong entity (" + signer.getSubjectDN() + ").\nExpected "
1362                              + signerCert.getSubjectDN());  
1363    }    
1364    return sc.getDataHandler();
1365  }  
1366  
1367  /**
1368   * Dumps the content of the original multipart message.
1369   * 
1370   * @param dh the dataHandler supplying the content of the original message
1371   * @throws IOException if an I/O error occurs while dumping the content
1372   * @throws MessagingException if an error occurs while reading the body parts of the message
1373   */
1374  public void dumpContent(DataHandler dh) throws IOException, MessagingException {
1375    Multipart mp = (Multipart)dh.getContent(); 
1376    System.out.println("Content is multipart (" + mp.getContentType() + ").");
1377    BodyPart bp1 = mp.getBodyPart(0);
1378    System.out.println("Content of first bodypart  (" + bp1.getContentType() + "):");
1379    System.out.println(bp1.getContent());
1380    BodyPart bp2 = mp.getBodyPart(1);
1381    System.out.println("Content of second bodypart  (" + bp2.getContentType() + "):");
1382    System.out.println(bp2.getContent());
1383    
1384  }  
1385  
1386  public SignedContent processMessageForMLA(Message msg, boolean implicit, String debugID) throws ESSLayerException, ESSException {
1387    
1388    // resolve the message into its layers
1389    ESSLayers layers = mla_.resolve(msg, debugID);
1390    try {
1391      return mla_.createSignedContent(signerPrivateKeyOfMLA_,
1392                                      new Date(),
1393                                      signerCertificatesOfMLA_[0], 
1394                                      signerCertificatesOfMLA_, 
1395                                      (AlgorithmID)AlgorithmID.sha256.clone(), 
1396                                      (AlgorithmID)AlgorithmID.rsaEncryption.clone(),
1397                                      encryptionCertificatesOfMLA_[0],
1398                                      true, 
1399                                      implicit,
1400                                      layers);
1401    } catch (Exception ex) {
1402      throw new ESSException("Error signing content: " + ex.toString()); 
1403    }  
1404  }  
1405  
1406  /**
1407   * Gets the data source encoding from the given data handler.
1408   *
1409   * @param dh the data handler from which to get the data source
1410   * 
1411   * @return the dataSource encoding; used for comparison
1412   *
1413   * @throws IOExceptio if an error occurs when reading the datasource
1414   */
1415  private byte[] getDataSource(DataHandler dh) throws IOException {
1416    DataSource ds = dh.getDataSource();
1417    ByteArrayOutputStream baos = new ByteArrayOutputStream();
1418    Utils.copyStream(ds.getInputStream(), baos, null);
1419    return baos.toByteArray();
1420  }  
1421
1422  
1423  
1424  /**
1425   * Creates a MLExpansionHistory containing only one MLData for
1426   * the given MLA with given expansion time and MLReceiptPolicy.
1427   *
1428   * @param mlaCertificate the certificate of the MLA from which to create the
1429   *        MLData EntityIdentiifier of type IssuerAndSerialNumber
1430   * @param expansionTime the expansion time
1431   * @param mlReceiptPolicy the MLReceiptPolicy; may be null
1432   *
1433   * @return the newly created MLExpansionHistory
1434   */
1435  public static MLExpansionHistory createMLExpansionHistory(X509Certificate mlaCertificate,
1436                                                     Date expansionTime,
1437                                                     MLReceiptPolicy mlReceiptPolicy) {
1438    
1439    IssuerAndSerialNumber ias = new IssuerAndSerialNumber(mlaCertificate);
1440    MLData mlData = new MLData(new EntityIdentifier(ias), expansionTime); 
1441    mlData.setMLReceiptPolicy(mlReceiptPolicy);
1442    return new MLExpansionHistory(mlData);
1443  }                                                       
1444  
1445  /**
1446   * Reads the MLExpansionHistory attribute from the given signed data and dumps the 
1447   * included MLData structures.
1448   *
1449   * @param signedContent the (MLA created) SignedContent to be parsed for the MLExpansionHistory
1450   *                      attribute
1451   * @param count the (expected) number of MLData entries included in the MLExpansionHistory attribute
1452   *
1453   * @throws Exception if an error occurs when parsing the MLExpansionHistory attribute, or if
1454   *                      no MLExpansionHistory attribute is inlcuded or if the MLExpansionHistory
1455   *                      does contain an unexpected number of MLData entries
1456   */
1457  public static void readMLExpansionHistory(SignedContent signedContent, int count) throws Exception {
1458    SignerInfo signerInfo = signedContent.getSignerInfos()[0]; 
1459    MLExpansionHistory mlExpansionHistory = (MLExpansionHistory)signerInfo.getSignedAttributeValue(MLExpansionHistory.oid);
1460    if (mlExpansionHistory == null) {
1461      throw new Exception("Missing MLExpansionHistory attribute");   
1462    }    
1463    int size = mlExpansionHistory.countMLDataEntries();
1464    if (count != size) {
1465      throw new Exception("Invalid number (" + size + ") of MLData entries. Expected " + count);   
1466    }    
1467    System.out.println(mlExpansionHistory.toString(true));
1468  }  
1469  
1470  /** 
1471   * Prints a dump of the given message to System.out.
1472   *
1473   * @param msg the message to be dumped to System.out
1474   */
1475  private static void dumpMessage(Message msg) throws IOException {
1476    System.out.println("******************************************************************");
1477    System.out.println("Message dump: \n");
1478    try {
1479      msg.writeTo(System.out);
1480    } catch (MessagingException ex) {
1481      throw new IOException(ex.getMessage());   
1482    }    
1483    System.out.println("\n******************************************************************");
1484  }  
1485  
1486  /**
1487   * Main method.
1488   */
1489  public static void main(String[] argv) throws Exception {
1490    try {
1491      DemoSMimeUtil.initDemos();
1492      (new MLADemo()).start();
1493    } catch (Exception ex) {
1494      ex.printStackTrace();   
1495    }       
1496    DemoUtil.waitKey();
1497  }
1498}