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/basic/SMimeOaepPssDemo.java 8     12.02.25 17:58 Dbratko $
059    // $Revision: 8 $
060    //
061    
062    package demo.smime.basic;
063    
064    import iaik.asn1.structures.AlgorithmID;
065    import iaik.cms.CMSException;
066    import iaik.cms.Utils;
067    import iaik.smime.EncryptedContent;
068    import iaik.smime.SMimeBodyPart;
069    import iaik.smime.SMimeMultipart;
070    import iaik.smime.SignedContent;
071    import iaik.x509.X509Certificate;
072    
073    import java.io.ByteArrayInputStream;
074    import java.io.ByteArrayOutputStream;
075    import java.io.IOException;
076    import java.security.InvalidAlgorithmParameterException;
077    import java.security.NoSuchAlgorithmException;
078    import java.security.PrivateKey;
079    import java.util.Date;
080    
081    import javax.activation.DataHandler;
082    import javax.activation.FileDataSource;
083    import javax.mail.Message;
084    import javax.mail.MessagingException;
085    import javax.mail.Multipart;
086    import javax.mail.Session;
087    import javax.mail.internet.InternetAddress;
088    import javax.mail.internet.MimeBodyPart;
089    import javax.mail.internet.MimeMessage;
090    
091    import demo.DemoSMimeUtil;
092    import demo.DemoUtil;
093    import demo.cms.envelopedData.OaepEnvelopedDataDemo;
094    import demo.cms.signedData.PssSignedDataDemo;
095    import demo.keystore.CMSKeyStore;
096    import demo.smime.DumpMessage;
097    
098    /**
099     * This class demonstrates the usage of the IAIK S/MIME implementation using RSA-PSS for
100     * signing, RSA-OAEP for key transport and AES-256 CBC for content encryption. It shows how to create
101     * signed and/or encrypted S/MIME messages and how to parse them and verify the signature 
102     * and decrypt the content, respectively.
103     * <p>
104     * To run this demo the following packages are required:
105     * <ul>
106     *    <li>
107     *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
108     *    </li>
109     *    <li>
110     *       <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>).
111     *    </li>
112     *    <li>
113     *       <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
114     *    </li>   
115     *    <li>
116     *       <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).
117     *    </li> 
118     * </ul>
119     */
120    public class SMimeOaepPssDemo {
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";                     // name of sender
126      String lastName_ = "SMime";
127      String from_ = "smimetest@iaik.tugraz.at";      // email sender
128      String to_ = "smimetest@iaik.tugraz.at";        // email recipient
129      String host_ = "mailhost";                      // name of the mailhost
130    
131      X509Certificate[] signerCertificates_;          // list of certificates to include in the S/MIME message
132      X509Certificate recipientCertificate_;          // certificate of the recipient
133      X509Certificate signerCertificate_;             // certificate of the signer/sender
134      PrivateKey signerPrivateKey_;                   // private key of the signer/sender
135      
136      AlgorithmID hashID_;                            // the hash algorithm to be used
137      int saltLength_;                                // the salt length to be used
138      
139      /**
140       * Default constructor. Reads certificates and keys from the demo keystore.
141       */
142      public SMimeOaepPssDemo() {
143        
144        System.out.println();
145        System.out.println("******************************************************************************************");
146        System.out.println("*                                 SMimeDemo demo                                         *");
147        System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIME messages) *");
148        System.out.println("******************************************************************************************");
149        System.out.println();
150        
151        // get the certificates from the KeyStore
152        signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
153        signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
154        signerCertificate_ = signerCertificates_[0];
155    
156        // recipient = signer for this test
157        recipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
158        
159        hashID_ = AlgorithmID.sha256;
160        saltLength_ = 32;
161      }
162      
163      /**
164       * Starts the demo.
165       *
166       * @throws IOException if an I/O related error occurs
167       */
168      public void start() throws IOException {
169    
170            // get the default Session
171            Session session = DemoSMimeUtil.getSession();
172    
173            try {
174          // Create a demo Multipart
175          MimeBodyPart mbp1 = new SMimeBodyPart();
176          mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
177              // attachment
178          MimeBodyPart attachment = new SMimeBodyPart();
179          attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
180          attachment.setFileName("test.html");
181            
182          Multipart mp = new SMimeMultipart();
183          mp.addBodyPart(mbp1);
184          mp.addBodyPart(attachment);
185          DataHandler multipart = new DataHandler(mp, mp.getContentType());
186    
187          Message msg;    // the message to send
188          ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
189          ByteArrayInputStream bais;  // we read from a stream
190    
191          // 1. This is a plain message
192          msg = createPlainMessage(session, multipart);
193          System.out.println("creating plain message...");
194              msg.saveChanges();
195              msg.writeTo(baos);
196              bais = new ByteArrayInputStream(baos.toByteArray());
197              msg = new MimeMessage(session, bais);
198              if (PRINT_MESSAGES) {
199            printMessage(msg);
200          }
201              DumpMessage.dumpMsg(msg);
202              
203              System.out.println("\n\n*****************************************\n\n");
204    
205          // 2. This is an explicitly signed message
206          msg = createSignedMessage(session, multipart, false);
207          System.out.println("creating explicitly signed message...");
208          baos.reset();
209              msg.saveChanges();
210              msg.writeTo(baos);
211              bais = new ByteArrayInputStream(baos.toByteArray());
212              msg = new MimeMessage(session, bais);
213              if (PRINT_MESSAGES) {
214            printMessage(msg);
215          }
216              DumpMessage.dumpMsg(msg);
217              
218              System.out.println("\n\n*****************************************\n\n");
219    
220    
221          // 3. This is an implicitly signed message
222          msg = createSignedMessage(session, multipart, true);
223          System.out.println("creating implicitly signed message...");
224              baos.reset();
225              msg.saveChanges();
226              msg.writeTo(baos);
227              bais = new ByteArrayInputStream(baos.toByteArray());
228              msg = new MimeMessage(session, bais);
229              if (PRINT_MESSAGES) {
230            printMessage(msg);
231          }
232              DumpMessage.dumpMsg(msg);
233              
234              System.out.println("\n\n*****************************************\n\n");
235    
236          // 4. Now create encrypted message
237          
238          msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
239          System.out.println("creating encrypted message [AES/256]...");
240          baos.reset();
241          msg.saveChanges();
242          msg.writeTo(baos);
243          bais = new ByteArrayInputStream(baos.toByteArray());
244          msg = new MimeMessage(session, bais);
245          if (PRINT_MESSAGES) {
246            printMessage(msg);
247          }
248          DumpMessage.dumpMsg(msg);
249          
250          System.out.println("\n\n*****************************************\n\n");
251       
252    
253    
254          // 5. Now create a implicitly signed and encrypted message with attachment
255          System.out.println("creating implicitly signed and encrypted message [AES/256]...");
256          msg = createSignedAndEncryptedMessage(session, multipart, true);
257              baos.reset();
258              msg.saveChanges();
259              msg.writeTo(baos);
260              bais = new ByteArrayInputStream(baos.toByteArray());
261              msg = new MimeMessage(session, bais);
262              if (PRINT_MESSAGES) {
263            printMessage(msg);
264          }
265              DumpMessage.dumpMsg(msg);
266              
267              System.out.println("\n\n*****************************************\n\n");
268    
269          // 6. Now create a explicitly signed and encrypted message with attachment
270              System.out.println("creating explicitly signed and encrypted message [AES/256]...");
271          msg = createSignedAndEncryptedMessage(session, multipart, false);
272              baos.reset();
273              msg.saveChanges();
274              msg.writeTo(baos);
275              bais = new ByteArrayInputStream(baos.toByteArray());
276              msg = new MimeMessage(session, bais);
277              if (PRINT_MESSAGES) {
278            printMessage(msg);
279          }
280              DumpMessage.dumpMsg(msg);
281              
282              System.out.println("\n\n*****************************************\n\n");
283    
284             
285    
286            } catch (Exception ex) {
287                ex.printStackTrace();
288                throw new RuntimeException(ex.toString());
289            }
290      }
291      
292      /**
293       * Creates a MIME message container with the given subject for the given session.
294       * 
295       * @param session the mail sesion
296       * @param subject the subject of the message
297       *
298       * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
299       *
300       * @throws MessagingException if the message cannot be created
301       */
302      public Message createMessage(Session session, String subject) throws MessagingException {
303        MimeMessage msg = new MimeMessage(session);
304        msg.setFrom(new InternetAddress(from_));
305            msg.setRecipients(Message.RecipientType.TO,     InternetAddress.parse(to_, false));
306            msg.setSentDate(new Date());
307        msg.setSubject(subject);
308        return msg;
309      }
310      
311      /**
312       * Creates a simple plain (neither signed nor encrypted) message.
313       *
314       * @param session the mail session
315       * @param dataHandler the content of the message
316       * 
317       * @return the plain message
318       *
319       * @throws MessagingException if an error occurs when creating the message
320       */
321      public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException {
322    
323        Message msg = createMessage(session, "IAIK-S/MIME: Plain message");
324        if (dataHandler != null) {
325          msg.setDataHandler(dataHandler);
326        } else {
327          msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n");
328        }
329            return msg;
330      }
331      
332      /**
333       * Creates a signed and encrypted message.
334       *
335       * @param session the mail session
336       * @param dataHandler the content of the message to be signed and encrypted
337       * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
338       *                 (multipart/signed) signing
339       * 
340       * @return the signed and encrypted message
341       *
342       * @throws MessagingException if an error occurs when creating the message
343       */
344      public Message createSignedAndEncryptedMessage(Session session, DataHandler dataHandler, boolean implicit)
345        throws MessagingException {
346    
347        String subject = null;
348        String text = null;
349        if (implicit) {
350          subject = "IAIK-S/MIME: Implicitly Signed and Encrypted";
351          text = "This message is implicitly signed and encrypted!\n\n\n";
352        } else {
353          subject = "IAIK-S/MIME: Explicitly Signed and Encrypted";
354          text = "This message is explicitly signed and encrypted!\n\n\n";
355        }
356        Message msg = createMessage(session, subject);
357    
358        SignedContent sc = new SignedContent(implicit);
359        if (dataHandler != null) {
360          sc.setDataHandler(dataHandler);
361        } else {
362          sc.setText(text);
363        }
364        sc.setCertificates(signerCertificates_);
365        try {
366          AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone();
367          AlgorithmID rsaPssID = PssSignedDataDemo.createPssAlgorithmID(hashID_, mgfID, saltLength_);
368          sc.addSigner(signerPrivateKey_, signerCertificate_, hashID_, rsaPssID);
369        } catch (NoSuchAlgorithmException ex) {
370          throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
371        } catch (InvalidAlgorithmParameterException ex) {
372          throw new MessagingException("Cannot create PSS parameters: " + ex.getMessage(), ex);
373        }
374    
375    
376        EncryptedContent ec = new EncryptedContent(sc);
377        // encrypt for the recipient
378        try {
379          // empty label
380          byte[] label = {};
381          AlgorithmID rsaOaepID = Utils.createOaepAlgorithmID(hashID_);
382          ec.addRecipient(recipientCertificate_, rsaOaepID);
383          ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
384        } catch (NoSuchAlgorithmException ex) {
385          throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
386        } catch (Exception ex) {
387          throw new MessagingException("Cannot create OAEP parameters: " + ex.getMessage());   
388        }    
389        msg.setContent(ec, ec.getContentType());
390        // let the EncryptedContent update some message headers
391        ec.setHeaders(msg);
392    
393        return msg;
394      }
395      
396      /**
397       * Creates a signed message.
398       *
399       * @param session the mail session
400       * @param dataHandler the content of the message to be signed
401       * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
402       *                 (multipart/signed) signing
403       * 
404       * @return the signed message
405       *
406       * @throws MessagingException if an error occurs when creating the message
407       */
408      public Message createSignedMessage(Session session, 
409          DataHandler dataHandler, boolean implicit)
410          throws MessagingException {
411    
412        String subject = null;
413        StringBuffer buf = new StringBuffer();
414        
415        if (implicit) {
416          subject = "IAIK-S/MIME: Implicitly Signed";
417          buf.append("This message is implicitly signed!\n");
418          buf.append("You need an S/MIME aware mail client to view this message.\n");
419          buf.append("\n\n");
420        } else {
421          subject = "IAIK-S/MIME: Explicitly Signed";
422          buf.append("This message is explicitly signed!\n");
423          buf.append("Every mail client can view this message.\n");
424          buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
425          buf.append("\n\n");
426        }
427        
428        Message msg = createMessage(session, subject);
429    
430        SignedContent sc = new SignedContent(implicit);
431    
432        if (dataHandler != null) {
433          sc.setDataHandler(dataHandler);
434        } else {
435          sc.setText(buf.toString());
436        }
437        sc.setCertificates(signerCertificates_);
438        
439        try {
440          AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone();
441          AlgorithmID rsaPssID = PssSignedDataDemo.createPssAlgorithmID(hashID_, mgfID, saltLength_);
442          sc.addSigner(signerPrivateKey_, signerCertificate_, hashID_, rsaPssID);
443        } catch (NoSuchAlgorithmException ex) {
444          throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
445        } catch (InvalidAlgorithmParameterException ex) {
446          throw new MessagingException("Cannot create PSS parameters: " + ex.getMessage(), ex);
447        }
448    
449        msg.setContent(sc, sc.getContentType());
450        // let the SignedContent update some message headers
451        sc.setHeaders(msg);
452        return msg;
453      }
454      
455      /**
456       * Creates an encrypted message.
457       *
458       * @param session the mail session
459       * @param algorithm the content encryption algorithm to be used
460       * @param keyLength the length of the secret content encryption key to be created and used
461       * 
462       * @return the encrypted message
463       *
464       * @throws MessagingException if an error occurs when creating the message
465       */
466      public Message createEncryptedMessage(Session session, AlgorithmID algorithm, int keyLength)
467          throws MessagingException {
468    
469        StringBuffer subject = new StringBuffer();
470        subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName());
471        if (keyLength > 0) {
472          subject.append("/"+keyLength);
473        }  
474        subject.append("]");
475        Message msg = createMessage(session, subject.toString());
476    
477        EncryptedContent ec = new EncryptedContent();
478    
479        StringBuffer buf = new StringBuffer();
480        buf.append("This is the encrypted content!\n");
481        buf.append("Content encryption algorithm: "+algorithm.getName());
482        buf.append("\n\n");
483    
484        ec.setText(buf.toString());
485        
486        // encrypt for the recipient
487        try {
488          AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone();
489          AlgorithmID pSourceID = (AlgorithmID)AlgorithmID.pSpecified.clone();
490          // empty label
491          byte[] label = {};
492          AlgorithmID rsaOaepID =  Utils.createOaepAlgorithmID(hashID_);
493          ec.addRecipient(recipientCertificate_, rsaOaepID);
494          ec.setEncryptionAlgorithm(algorithm, keyLength);
495        } catch (NoSuchAlgorithmException ex) {
496          throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
497        } catch (Exception ex) {
498          throw new MessagingException("Cannot create OAEP parameters: " + ex.getMessage());   
499        }    
500    
501        msg.setContent(ec, ec.getContentType());
502        // let the EncryptedContent update some message headers
503        ec.setHeaders(msg);
504    
505        return msg;
506      }
507    
508      
509     
510      
511      
512      /** 
513       * Prints a dump of the given message to System.out.
514       *
515       * @param msg the message to be dumped to System.out
516       */
517      static void printMessage(Message msg) throws IOException {
518        System.out.println("------------------------------------------------------------------");
519        System.out.println("Message dump: \n");
520        try {
521          msg.writeTo(System.out);
522        } catch (MessagingException ex) {
523          throw new IOException(ex.getMessage());   
524        }    
525        System.out.println("\n------------------------------------------------------------------");
526      }  
527    
528    
529      /**
530       * The main method.
531       */
532      public static void main(String[] argv) throws IOException {
533    
534        DemoSMimeUtil.initDemos();
535            (new SMimeOaepPssDemo()).start();
536        System.out.println("\nReady!");
537        DemoUtil.waitKey();
538      }
539    }