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}