001    // Copyright (C) 2002 IAIK
002    // https://jce.iaik.tugraz.at
003    //
004    // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005    //                           Communication Technologies SIC
006    // https://sic.tech
007    //
008    // All rights reserved.
009    //
010    // Redistribution and use in source and binary forms, with or without
011    // modification, are permitted provided that the following conditions
012    // are met:
013    // 1. Redistributions of source code must retain the above copyright
014    //    notice, this list of conditions and the following disclaimer.
015    // 2. Redistributions in binary form must reproduce the above copyright
016    //    notice, this list of conditions and the following disclaimer in the
017    //    documentation and/or other materials provided with the distribution.
018    //
019    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029    // SUCH DAMAGE.
030    
031    // Copyright (C) 2002 IAIK
032    // https://sic.tech/
033    //
034    // Copyright (C) 2003 - 2025 Stiftung Secure Information and 
035    //                           Communication Technologies SIC
036    // https://sic.tech/
037    //
038    // All rights reserved.
039    //
040    // This source is provided for inspection purposes and recompilation only,
041    // unless specified differently in a contract with IAIK. This source has to
042    // be kept in strict confidence and must not be disclosed to any third party
043    // under any circumstances. Redistribution in source and binary forms, with
044    // or without modification, are <not> permitted in any case!
045    //
046    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056    // SUCH DAMAGE.
057    //
058    // $Header: /IAIK-CMS/current/src/demo/smime/ess/TripleWrappingDemo.java 38    12.02.25 17:59 Dbratko $
059    // $Revision: 38 $
060    //
061    
062    package demo.smime.ess;
063    
064    import iaik.asn1.structures.AlgorithmID;
065    import iaik.smime.EncryptedContent;
066    import iaik.smime.SMimeBodyPart;
067    import iaik.smime.SMimeMultipart;
068    import iaik.smime.SignedContent;
069    import iaik.x509.X509Certificate;
070    
071    import java.io.ByteArrayInputStream;
072    import java.io.ByteArrayOutputStream;
073    import java.io.IOException;
074    import java.security.NoSuchAlgorithmException;
075    import java.security.PrivateKey;
076    import java.security.interfaces.RSAPrivateKey;
077    import java.util.Date;
078    
079    import javax.activation.DataHandler;
080    import javax.activation.FileDataSource;
081    import javax.mail.Message;
082    import javax.mail.MessagingException;
083    import javax.mail.Multipart;
084    import javax.mail.Session;
085    import javax.mail.internet.InternetAddress;
086    import javax.mail.internet.MimeBodyPart;
087    import javax.mail.internet.MimeMessage;
088    
089    import demo.DemoSMimeUtil;
090    import demo.DemoUtil;
091    import demo.keystore.CMSKeyStore;
092    import demo.smime.DumpMessage;
093    
094    /**
095     * An ESS triple wrapping demo. Creates a <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a> 
096     * ESS triple wrapped (signed - encrypted - signed) message and subsequently parses it to 
097     * decrypt the layer and verify the signatures.
098     * <p>
099     * To run this demo the following packages are required:
100     * <ul>
101     *    <li>
102     *       <code>iaik_cms.jar</code>
103     *    </li>
104     *    <li>
105     *       <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>).
106     *    </li>
107     *    <li>
108     *       <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
109     *    </li>   
110     *    <li>
111     *       <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).
112     *    </li> 
113     * </ul>
114     *
115     * @see iaik.smime.EncryptedContent
116     * @see iaik.smime.SignedContent
117     */
118    public class TripleWrappingDemo {
119        
120      final static boolean DEBUG = false;
121       
122      // whether to print dump all generates test messages to System.out
123      final static boolean PRINT_MESSAGES = false;
124    
125      String firstName = "John";
126      String lastName = "SMime";
127      String to = "smimetest@iaik.at";     // email recipient
128      String from = "smimetest@iaik.at";   // email sender
129      String host = "mailhost";                       // name of the mailhost
130    
131      X509Certificate[] signerCertificates1;          // list of certificates to include in the S/MIME message
132      X509Certificate recipientCertificate;          // certificate of the recipient
133      X509Certificate signerCertificate1;             // certificate of the signer/sender
134      X509Certificate encryptionCertOfSigner1;        // signer uses different certificate for encryption
135      PrivateKey signerPrivateKey1;                   // private key of the signer/sender
136      
137      X509Certificate[] signerCertificates2;          // if outer signer is different than inner signer
138      X509Certificate signerCertificate2;             // certificate of the signer/sender
139      PrivateKey signerPrivateKey2;                   // private key of the signer/sender
140      
141      /**
142       * Empty default constructor. Reads all required keys and certificates
143       * from the demo keystore (created by running @link demo.keystore.SetupCMSKeySrore)
144       * stored at "cms.keystore" in your current working directoy.
145       */
146      public TripleWrappingDemo() {
147        
148        System.out.println();
149        System.out.println("******************************************************************************************");
150        System.out.println("*                             TripleWrapping demo                                        *");
151        System.out.println("* (shows the usage of the IAIK-CMS library for creating/parsing a triple wrapped message *");
152        System.out.println("******************************************************************************************");
153        System.out.println();
154        
155        // get the certificates from the KeyStore
156        signerCertificates1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
157        signerPrivateKey1 = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
158        signerCertificate1 = signerCertificates1[0];
159    
160        // recipient = signer for this test
161        recipientCertificate = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
162        encryptionCertOfSigner1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
163        
164        signerCertificates2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
165        signerPrivateKey2 = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2);
166        signerCertificate2 = signerCertificates2[0];
167      }
168      
169      /**
170       * Starts the demo.
171       *
172       * @throws IOException if an I/O related error occurs
173       */
174      public void start() throws IOException {
175    
176            // get the default Session
177            Session session = DemoSMimeUtil.getSession();
178    
179            try {
180          // Create a demo Multipart
181          MimeBodyPart mbp1 = new SMimeBodyPart();
182          mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
183          MimeBodyPart attachment = new SMimeBodyPart();
184          attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
185          attachment.setFileName("test.html");      
186          
187          Multipart mp = new SMimeMultipart();
188          mp.addBodyPart(mbp1);
189          mp.addBodyPart(attachment);
190          DataHandler multipart = new DataHandler(mp, mp.getContentType());
191    
192          Message msg;    // the message to send
193          ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
194          ByteArrayInputStream bais;  // we read from a stream
195      
196          // 1. implicitly signed - encrypted - implicitly signed; inner = outer signed
197          System.out.println("1. implicitly signed - encrypted - implicitly signed; inner = outer signed");
198          msg = tripleWrap(session, multipart, true, true, false);
199              msg.writeTo(baos);
200              bais = new ByteArrayInputStream(baos.toByteArray());
201              msg = new MimeMessage(session, bais);
202              if (PRINT_MESSAGES) {
203                printMessage(msg);
204              }  
205              DumpMessage.dumpMsg(msg);
206              baos.reset();
207              System.out.println("\n\n*****************************************\n\n");
208              
209                // 2. implicitly signed - encrypted - implicitly signed; inner != outer signed
210          System.out.println("2. implicitly signed - encrypted - implicitly signed; inner != outer signed");
211          msg = tripleWrap(session, multipart, true, true, true);
212              msg.writeTo(baos);
213              bais = new ByteArrayInputStream(baos.toByteArray());
214              msg = new MimeMessage(session, bais);
215              if (PRINT_MESSAGES) {
216                printMessage(msg);
217              }  
218              DumpMessage.dumpMsg(msg);
219              baos.reset();
220              System.out.println("\n\n*****************************************\n\n");
221              
222                // 3. implicitly signed - encrypted - explicitly signed; inner == outer signed
223          System.out.println("3. implicitly signed - encrypted - explicitly signed; inner = outer signed");
224          msg = tripleWrap(session, multipart, true, false, false);
225              msg.writeTo(baos);
226              bais = new ByteArrayInputStream(baos.toByteArray());
227              msg = new MimeMessage(session, bais);
228              if (PRINT_MESSAGES) {
229                printMessage(msg);
230              }
231              DumpMessage.dumpMsg(msg);
232              baos.reset();
233              System.out.println("\n\n*****************************************\n\n");
234              
235              // 4. implicitly signed - encrypted - explicitly signed; inner != outer signed
236          System.out.println("4. implicitly signed - encrypted - explicitly signed; inner != outer signed");
237          msg = tripleWrap(session, multipart, true, false, true);
238              msg.writeTo(baos);
239              bais = new ByteArrayInputStream(baos.toByteArray());
240              msg = new MimeMessage(session, bais);
241              if (PRINT_MESSAGES) {
242                printMessage(msg);
243              }
244              DumpMessage.dumpMsg(msg);
245              baos.reset();
246              System.out.println("\n\n*****************************************\n\n");
247              
248              // 5. explicitly signed - encrypted - implicitly signed; inner == outer signed
249          System.out.println("5. explicitly signed - encrypted - implicitly signed; inner = outer signed");
250          msg = tripleWrap(session, multipart, false, true, false);
251              msg.writeTo(baos);
252              bais = new ByteArrayInputStream(baos.toByteArray());
253              msg = new MimeMessage(session, bais);
254              if (PRINT_MESSAGES) {
255                printMessage(msg);
256              }
257              DumpMessage.dumpMsg(msg);
258              baos.reset();
259              System.out.println("\n\n*****************************************\n\n");
260              
261              // 6. explicitly signed - encrypted - implicitly signed; inner != outer signed
262          System.out.println("6. explicitly signed - encrypted - implicitly signed; inner != outer signed");
263          msg = tripleWrap(session, multipart, false, true, true);
264              msg.writeTo(baos);
265              bais = new ByteArrayInputStream(baos.toByteArray());
266              msg = new MimeMessage(session, bais);
267              if (PRINT_MESSAGES) {
268                printMessage(msg);
269              }
270              DumpMessage.dumpMsg(msg);
271              baos.reset();
272              System.out.println("\n\n*****************************************\n\n");
273              
274              // 7. explicitly signed - encrypted - explicitly signed; inner == outer signed
275          System.out.println("7. explicitly signed - encrypted - explicitly signed; inner = outer signed");
276          msg = tripleWrap(session, multipart, false, false, false);
277              msg.writeTo(baos);
278              bais = new ByteArrayInputStream(baos.toByteArray());
279              msg = new MimeMessage(session, bais);
280              if (PRINT_MESSAGES) {
281                printMessage(msg);
282              }
283              DumpMessage.dumpMsg(msg);
284          baos.reset();
285              System.out.println("\n\n*****************************************\n\n");
286              
287              // 8. explicitly signed - encrypted - explicitly signed; inner != outer signed
288          System.out.println("8. explicitly signed - encrypted - explicitly signed; inner != outer signed");
289          msg = tripleWrap(session, multipart, false, false, true);
290              msg.writeTo(baos);
291              bais = new ByteArrayInputStream(baos.toByteArray());
292              msg = new MimeMessage(session, bais);
293              if (PRINT_MESSAGES) {
294                printMessage(msg);
295              }
296              DumpMessage.dumpMsg(msg);
297              baos.reset();
298              System.out.println("\n\n*****************************************\n\n");
299          
300          System.out.println("Ready!");
301          
302            } catch (Exception ex) {
303          ex.printStackTrace();
304              throw new RuntimeException(ex.toString());
305            }
306      }
307    
308      /**
309       * Creates a MIME message container with the given subject for the given session.
310       * 
311       * @param session the mail sesion
312       * @param subject the subject of the message
313       *
314       * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
315       *
316       * @throws MessagingException if the message cannot be created
317       */
318      public Message createMessage(Session session, String subject) throws MessagingException {
319        MimeMessage msg = new MimeMessage(session);
320        msg.setFrom(new InternetAddress(from));
321              msg.setRecipients(Message.RecipientType.TO,   InternetAddress.parse(to, false));
322              msg.setSentDate(new Date());
323        msg.setSubject(subject);
324        return msg;
325      }
326    
327      /**
328       * Creates a triple wrapped (signed - encrypted - signed) message.
329       *
330       * @param session the Session
331       * @param dataHandler the data handler providing the raw content
332       * @param innerImplicit whether to sign the inner content implicitly or explicitly
333       * @param outerImplicit whether to sign the outer content implicitly or explicitly
334       * @param differentOuterSigner whether to simulate a different outer signer receiving
335       *                             the signed and encrypted message and adding an outer
336       *                             layer
337       *
338       * @return the triple wrapped message
339       */
340      public Message tripleWrap(Session session, DataHandler dataHandler, 
341                                boolean innerImplicit, boolean outerImplicit,
342                                boolean differentOuterSigner)
343        throws Exception {
344    
345        
346        StringBuffer buf = new StringBuffer();
347        String subject = "IAIK-S/MIME: TripleWrapped:  ";
348        buf.append("This is a triple wrapped message where  ");
349        if (innerImplicit) {
350          subject += "implicit signed - encrypted - "; 
351          buf.append("the inner content is implicit signed\n");
352        } else {
353          subject += "explicit signed - encrypted - "; 
354          buf.append("the inner content is explicit signed\n");
355        }
356        if (outerImplicit) {
357          subject += "implicit signed"; 
358          buf.append("and the outer content is implicit signed\n");
359        } else {
360          subject += "explicit signed"; 
361          buf.append("and the outer content is explicit signed\n");
362        }
363        
364        Message msg = createMessage(session, subject);
365        
366        
367        // create the inner signed content
368        SignedContent sc1 = new SignedContent(innerImplicit);
369        if (dataHandler != null) {
370          sc1.setDataHandler(dataHandler);
371        } else {
372          sc1.setText(buf.toString());
373        }
374        sc1.setCertificates(signerCertificates1);
375        try {
376          sc1.addSigner((RSAPrivateKey)signerPrivateKey1, signerCertificate1);
377        } catch (NoSuchAlgorithmException ex) {
378          throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
379        }
380        
381        // create the enrypted ("middle") layer
382        EncryptedContent ec = new EncryptedContent(sc1);
383        ec.setSMimeType();
384        // encrypt for the recipient
385        ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
386        // I want to be able to decrypt the message, too
387        ec.addRecipient(encryptionCertOfSigner1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
388        // set the encryption algorithm
389        ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
390        
391        if (differentOuterSigner) {
392          // just do a receiving sending step inbetween
393          Message msg1 = createMessage(session, "IAIK-S/MIME: Signed and encrypted");
394          msg1.setContent(ec, ec.getContentType());
395          ec.setHeaders(msg1);
396          msg1.saveChanges();
397          ByteArrayOutputStream baos = new ByteArrayOutputStream();
398          msg1.writeTo(baos);
399          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
400          msg1 = new MimeMessage(session, bais);
401          if (PRINT_MESSAGES) {
402            printMessage(msg1);
403          }  
404          // optionally parse message here
405                DumpMessage.dumpMsg(msg1);
406                ec = (EncryptedContent)msg1.getContent();
407        }    
408        
409        // create the signed outer layer
410        SignedContent sc2 = new SignedContent(ec, outerImplicit);
411        
412        if (differentOuterSigner) {
413          sc2.setCertificates(signerCertificates2);
414          try {
415            sc2.addSigner((RSAPrivateKey)signerPrivateKey2, signerCertificate2);
416          } catch (NoSuchAlgorithmException ex) {
417            throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
418          }  
419        } else {    
420          sc2.setCertificates(signerCertificates1);
421          try {
422            sc2.addSigner((RSAPrivateKey)signerPrivateKey1, signerCertificate1);
423          } catch (NoSuchAlgorithmException ex) {
424            throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
425          }
426        }
427        msg.setContent(sc2, sc2.getContentType());
428        // let the EncryptedContent update some message headers
429        sc2.setHeaders(msg);
430        msg.saveChanges();
431        return msg;
432      }
433    
434      /** 
435       * Prints a dump of the given message to System.out.
436       *
437       * @param msg the message to be dumped to System.out
438       */
439      private static void printMessage(Message msg) throws IOException {
440        System.out.println("------------------------------------------------------------------");
441        System.out.println("Message dump: \n");
442        try {
443          msg.writeTo(System.out);
444        } catch (MessagingException ex) {
445          throw new IOException(ex.getMessage());   
446        }    
447        System.out.println("\n------------------------------------------------------------------");
448      }  
449      
450      /**
451       * Main method.
452       */
453      public static void main(String[] argv) throws IOException {
454    
455        DemoSMimeUtil.initDemos();
456            (new TripleWrappingDemo()).start();
457        System.out.println("\nReady!");
458        DemoUtil.waitKey();
459      }
460    }