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/SMimeShowDemo.java 30    12.02.25 17:58 Dbratko $
059    // $Revision: 30 $
060    //
061    
062    package demo.smime.basic;
063    
064    import iaik.smime.TrustVerifier;
065    import iaik.x509.X509Certificate;
066    
067    import java.io.IOException;
068    import java.security.cert.CertificateException;
069    import java.util.Date;
070    
071    import javax.mail.Address;
072    import javax.mail.FetchProfile;
073    import javax.mail.Flags;
074    import javax.mail.Folder;
075    import javax.mail.Message;
076    import javax.mail.MessagingException;
077    import javax.mail.Session;
078    import javax.mail.Store;
079    import javax.mail.URLName;
080    
081    import demo.DemoSMimeUtil;
082    import demo.DemoUtil;
083    import demo.cms.ecc.ECCDemoUtil;
084    import demo.keystore.CMSKeyStore;
085    import demo.smime.DumpMessage;
086    
087    /**
088     * This class demonstrates the usage of the IAIK S/MIME implementation for downloading
089     * and verifying/decrypting signed and/or encrypted emails from some mail server.
090     * <p>
091     * To run this demo the following packages are required:
092     * <ul>
093     *    <li>
094     *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
095     *    </li>
096     *    <li>
097     *       <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>).
098     *    </li>
099     *    <li>
100     *       <code>iaik_eccelerate.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">IAIK ECC Library</a>, if you want to use Elliptic Curve Cryptography).
101     *    </li>
102     *    <li>
103     *       <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
104     *    </li>   
105     *    <li>
106     *       <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).
107     *    </li> 
108     * </ul>
109     *
110     * <b>Usage:</b>
111     * <pre>
112     * SMimeShow [-L url] [-T protocol] [-H host] [-U user] [-P password] [-f mailbox] [msgnum] [-v]
113     * </pre>
114     * <b>Example</b> to display message 2:
115     * <pre>
116     * SMimeShow -T imap -H mailhost -U test -P test -f INBOX 2
117     * </pre>
118     *
119     * @see iaik.smime.EncryptedContent
120     * @see iaik.smime.SignedContent
121     */
122    public class SMimeShowDemo {
123    
124      String from;
125      String protocol;
126      String host = null;
127      String user = null;
128      String password = null;
129      String mbox = "INBOX";
130      String url = null;
131      boolean verbose = false;
132      
133      /**
134       * Simple trust verifier.
135       */
136      private static TrustVerifier trustVerifier_ = null;
137      
138      
139      
140      /**
141       * Default constructor. Reads certificates and keys from the demo keystore.
142       */
143      public SMimeShowDemo() {
144        
145        System.out.println();
146        System.out.println("******************************************************************************************");
147        System.out.println("*                                 SMimeShow demo                                         *");
148        System.out.println("*     (shows how to parse and verify/decrypt signed and/or encrypted S/MIME messages)    *");
149        System.out.println("******************************************************************************************");
150        System.out.println();
151    
152        
153        trustVerifier_ = new TrustVerifier();
154        // set demo CAs as trusted
155        trustVerifier_.addTrustedCertificate(CMSKeyStore.getCaCertificate(CMSKeyStore.RSA));
156        trustVerifier_.addTrustedCertificate(CMSKeyStore.getCaCertificate(CMSKeyStore.DSA));
157        trustVerifier_.addTrustedCertificate(CMSKeyStore.getCaCertificate(CMSKeyStore.RSAPSS));
158        
159        // if EC is available set EC demo CAs as trusted, too
160        try {
161          trustVerifier_.addTrustedCertificate(demo.cms.ecc.keystore.CMSEccKeyStore.getCaCertificate());
162        } catch (Exception e) {
163          System.err.println("Cannot add EC demo CAs (EC not available?): " + e.toString());
164        }
165      }
166      
167      /**
168       * Connects to the mail server, downloads messages, verifies/decrypts
169       * them (if they are signed/encrypted).
170       *
171       * @param argv optional parameters like mailhost, account name,...
172       *
173       * @throws IOException if an I/O related error occurs
174       */
175      public void show(String[] argv) throws IOException {
176        
177            int msgnum = -1;
178            int optind = 0;
179        
180        // some defaults
181        protocol = "pop3";
182        host = "mailhost";
183        verbose = true;
184    
185        if (argv.length > 0) {
186              for (optind = 0; optind < argv.length; optind++) {
187                  if (argv[optind].equals("-T")) {
188                      protocol = argv[++optind];
189                  } else if (argv[optind].equals("-H")) {
190                          host = argv[++optind];
191                  } else if (argv[optind].equals("-U")) {
192                      user = argv[++optind];
193                  } else if (argv[optind].equals("-P")) {
194                          password = argv[++optind];
195                  } else if (argv[optind].equals("-v")) {
196                      verbose = true;
197                  } else if (argv[optind].equals("-f")) {
198                          mbox = argv[++optind];
199                  } else if (argv[optind].equals("-L")) {
200                      url = argv[++optind];
201                  } else if (argv[optind].equals("--")) {
202                      optind++;
203                      break;
204                  } else if (argv[optind].startsWith("-")) {
205                      System.out.println("Usage: SMimeShow [-L url] [-T protocol] [-H host] [-U user] [-P password] [-f mailbox] [msgnum] [-v]");
206                          System.exit(1);
207                  } else {
208                      break;
209                  }
210                }
211        } 
212    
213        try {
214                if (optind < argv.length)
215             msgnum = Integer.parseInt(argv[optind]);
216    
217                // get the default Session
218                Session session = DemoSMimeUtil.getSession();
219    
220                // Get a Store object
221                Store store = null;
222                if (url != null) {
223                    URLName urln = new URLName(url);
224                    store = session.getStore(urln);
225                    store.connect();
226                } else {
227              if (protocol != null) {
228                        store = session.getStore(protocol);
229                      } else {
230                        store = session.getStore();
231              }
232              // Connect
233              if (host != null || user != null || password != null) {
234                        store.connect(host, user, password);
235              }     else {
236                        store.connect();
237                      }  
238                }
239    
240                // Open the Folder
241                Folder folder = store.getDefaultFolder();
242                if (folder == null) {
243            System.out.println("No default folder");
244            System.exit(1);
245                }
246    
247                folder = folder.getFolder(mbox);
248                if (folder == null) {
249            System.out.println("Invalid folder");
250            System.exit(1);
251                }
252    
253    //          folder.open(Folder.READ_WRITE);
254                folder.open(Folder.READ_ONLY);            // only READ for POP3
255                int totalMessages = folder.getMessageCount();
256    
257                if (totalMessages == 0) {
258                    System.out.println("Empty folder");
259                    folder.close(false);
260                    store.close();
261                    System.exit(1);
262                }
263    
264                if (verbose) {
265                    int newMessages = folder.getNewMessageCount();
266                    System.out.println("Total messages = " + totalMessages);
267                    System.out.println("New messages = " + newMessages);
268                    System.out.println("-------------------------------");
269                }
270    
271                if (msgnum == -1) {
272                    // Attributes & Flags for all messages ..
273                    Message[] msgs = folder.getMessages();
274    
275                    // Use a suitable FetchProfile
276                    FetchProfile fp = new FetchProfile();
277                    fp.add(FetchProfile.Item.ENVELOPE);
278                    fp.add(FetchProfile.Item.FLAGS);
279                    fp.add("X-Mailer");
280                    folder.fetch(msgs, fp);
281    
282                    for (int i = 0; i < msgs.length; i++) {
283    
284                          System.out.println("--------------------------");
285                          System.out.println("MESSAGE #" + (i + 1) + ":");
286                          from = msgs[i].getFrom()[0].toString();
287                          dump(msgs[i]);
288              }
289                } else {
290              System.out.println("Getting message number: " + msgnum);
291              Message m = folder.getMessage(msgnum);
292              from = m.getFrom()[0].toString();
293              dump(m);
294                }
295    
296                folder.close(false);
297                store.close();
298    
299              // System.in.read();
300            } catch (Exception ex) {
301                ex.printStackTrace();
302                throw new RuntimeException(ex.toString());
303            }
304    
305      }
306      
307      /**
308       * Dumps the given object (message).
309       *
310       * @param o the object (message) to be dumped
311       *
312       * @throws Exception if some error occurs
313       */
314      public void dump(Object o) throws Exception {
315        DumpMessage dumpMsg = new DumpMessage();
316        dumpMsg.dump(o);
317        
318        // message signed and signer certs included?
319        X509Certificate[] signerCerts = dumpMsg.getSignerCerts();
320        if (signerCerts != null) {
321          try {
322            trustVerifier_.verifyCertificateChain(signerCerts);
323            System.out.println("Certificate chain trusted!");
324          } catch (CertificateException ex) {
325            System.out.println("Certificate chain not trusted!");
326          } 
327          // is email in cert equal to email from From: header?
328          // the email has to be formatted as an "addr-spec" as defined in RFC 822.
329          // An addr-spec has the form "local-part@domain".
330          if (trustVerifier_.checkEMail(from, signerCerts[0])) {
331             System.out.println("EMail is ok!");
332          } else {
333             System.out.println("EMail not ok!");
334          }
335        }
336      }
337      
338      /**
339       * Prints the envelope of a message.
340       * 
341       * @param m the message
342       * 
343       * @throws MessagingException if an error occurs
344       */
345      public static void dumpEnvelope(Message m) throws MessagingException {
346            System.out.println("This is the message envelope");
347            System.out.println("---------------------------");
348            Address[] a;
349              // FROM
350            if ((a = m.getFrom()) != null) {
351                for (int j = 0; j < a.length; j++)
352                    System.out.println("FROM: " + a[j].toString());
353            }
354    
355        // TO
356        if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
357                for (int j = 0; j < a.length; j++)
358                    System.out.println("TO: " + a[j].toString());
359            }
360    
361            // SUBJECT
362            System.out.println("SUBJECT: " + m.getSubject());
363    
364            // DATE
365            Date d = m.getSentDate();
366            System.out.println("SendDate: "+(d != null ? d.toString() : "UNKNOWN"));
367    
368        // SIZE
369        System.out.println("Size: " + m.getSize());
370    
371            // FLAGS:
372            Flags flags = m.getFlags();
373            StringBuffer sb = new StringBuffer();
374            Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags
375    
376            boolean first = true;
377              for (int i = 0; i < sf.length; i++) {
378                String s;
379                Flags.Flag f = sf[i];
380                if (f == Flags.Flag.ANSWERED)
381                    s = "\\Answered";
382                else if (f == Flags.Flag.DELETED)
383                        s = "\\Deleted";
384                else if (f == Flags.Flag.DRAFT)
385                    s = "\\Draft";
386                else if (f == Flags.Flag.FLAGGED)
387                        s = "\\Flagged";
388                else if (f == Flags.Flag.RECENT)
389                    s = "\\Recent";
390                else if (f == Flags.Flag.SEEN)
391                        s = "\\Seen";
392                else
393                    continue;       // skip it
394                if (first)
395                    first = false;
396                else
397                        sb.append(' ');
398                sb.append(s);
399            }
400    
401            String[] uf = flags.getUserFlags(); // get the user flag strings
402            for (int i = 0; i < uf.length; i++) {
403                if (first)
404                    first = false;
405                else
406                        sb.append(' ');
407                sb.append(uf[i]);
408            }
409              System.out.println("FLAGS = " + sb.toString());
410    
411            // X-MAILER
412            String[] hdrs = m.getHeader("X-Mailer");
413            if (hdrs != null)
414                System.out.println("X-Mailer: " + hdrs[0]);
415            else
416                System.out.println("X-Mailer NOT available");
417      }
418      
419      /** 
420       * Main method.
421       */
422      public static void main(String argv[]) throws Exception {
423        DemoSMimeUtil.initDemos();
424        // add ECC provider    
425        ECCDemoUtil.installIaikEccProvider();
426        (new SMimeShowDemo()).show(argv);
427        System.out.println("\nReady!");
428        DemoUtil.waitKey();
429      }
430    }