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