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/ProcessMessageDemo.java 20 12.02.25 17:58 Dbratko $ 029// $Revision: 20 $ 030// 031 032package demo.smime.basic; 033 034import iaik.asn1.structures.AlgorithmID; 035import iaik.cms.CMSAlgorithmID; 036import iaik.smime.CompressedContent; 037import iaik.smime.EncryptedContent; 038import iaik.smime.SMimeBodyPart; 039import iaik.smime.SMimeMultipart; 040import iaik.smime.SMimeParameters; 041import iaik.smime.SignedContent; 042import iaik.x509.X509Certificate; 043 044import java.io.ByteArrayInputStream; 045import java.io.ByteArrayOutputStream; 046import java.io.IOException; 047import java.security.NoSuchAlgorithmException; 048import java.security.PrivateKey; 049import java.security.interfaces.RSAPrivateKey; 050import java.util.Date; 051 052import jakarta.activation.DataHandler; 053import jakarta.activation.FileDataSource; 054import jakarta.mail.Message; 055import jakarta.mail.MessagingException; 056import jakarta.mail.Multipart; 057import jakarta.mail.Session; 058import jakarta.mail.internet.InternetAddress; 059import jakarta.mail.internet.MimeBodyPart; 060import jakarta.mail.internet.MimeMessage; 061 062import demo.DemoSMimeUtil; 063import demo.DemoUtil; 064import demo.keystore.CMSKeyStore; 065import demo.smime.DumpMessage; 066 067/** 068 * This class demonstrates the usage of the IAIK S/MIME implementation for 069 * cryptographically processing (e.g. signing or encrypting) a received 070 * message. Since the message to be processed has a -- already canonicalized -- 071 * multipart content, the SMimeMultipart/SMimeBodyPart control can be disabled 072 * either globally for the whole application: 073 * <pre> 074 * SMimeParameters.setCheckForSMimeParts(false); 075 * </pre> 076 * or only for the specific SignedContent object(s) in use: 077 * <pre> 078 * SignedContent sc = ...; 079 * sc.checkForSMimeParts(false); 080 * ... 081 * </pre> 082 * To run this demo the following packages are required: 083 * <ul> 084 * <li> 085 * <code>iaik_cms.jar</code> (IAIK-CMS/SMIME) 086 * </li> 087 * <li> 088 * <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>). 089 * </li> 090 * <li> 091 * <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 092 * </li> 093 * <li> 094 * <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a> 095 * </li> 096 * </ul> 097 */ 098public class ProcessMessageDemo { 099 100 // whether to print all generates test messages to System.out 101 final static boolean PRINT_MESSAGES = true; 102 103 String firstName = "John"; 104 String lastName = "SMime"; 105 String to = "smimetest@iaik.at"; // email recipient 106 String from = "smimetest@iaik.at"; // email sender 107 String host = "mailhost"; // name of the mailhost 108 109 X509Certificate[] signerCertificates; // list of certificates to include in the S/MIME message 110 X509Certificate recipientCertificate; // certificate of the recipient 111 X509Certificate signerCertificate; // certificate of the signer/sender 112 X509Certificate encryptionCertOfSigner; // signer uses different certificate for encryption 113 PrivateKey signerPrivateKey; // private key of the signer/sender 114 115 /** 116 * Default constructor. Reads certificates and keys from the demo keystore. 117 */ 118 public ProcessMessageDemo() { 119 120 System.out.println(); 121 System.out.println("******************************************************************************************"); 122 System.out.println("* ProcessMessageDemo *"); 123 System.out.println("* (shows how to cryptographically process (sign, verify) an existing message) *"); 124 System.out.println("******************************************************************************************"); 125 System.out.println(); 126 127 // get the certificates from the KeyStore 128 signerCertificates = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 129 signerPrivateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 130 signerCertificate = signerCertificates[0]; 131 132 // recipient = signer for this test 133 recipientCertificate = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 134 encryptionCertOfSigner = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 135 136 // we will cryptographically process an already existing (canonicalized) 137 // message and therefore can disable SMimeMultipart/SMimeBodyPart control 138 SMimeParameters.setCheckForSMimeParts(false); 139 } 140 141 /** 142 * Starts the demo. 143 * 144 * @throws IOException if an I/O related error occurs 145 */ 146 public void start() throws IOException { 147 148 // get the default Session 149 Session session = DemoSMimeUtil.getSession(); 150 151 try { 152 // Create a demo Multipart 153 MimeBodyPart mbp1 = new SMimeBodyPart(); 154 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n"); 155 // attachment 156 MimeBodyPart attachment = new SMimeBodyPart(); 157 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html"))); 158 attachment.setFileName("test.html"); 159 160 Multipart mp = new SMimeMultipart(); 161 mp.addBodyPart(mbp1); 162 mp.addBodyPart(attachment); 163 DataHandler multipart = new DataHandler(mp, mp.getContentType()); 164 165 Message msg; // the message to send 166 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 167 ByteArrayInputStream bais; // we read from a stream 168 169 // Create the plain test message 170 msg = createPlainMessage(session, multipart); 171 System.out.println("creating plain message..."); 172 msg.saveChanges(); 173 msg.writeTo(baos); 174 bais = new ByteArrayInputStream(baos.toByteArray()); 175 msg = new MimeMessage(session, bais); 176 if (PRINT_MESSAGES) { 177 printMessage(msg); 178 } 179 DumpMessage.dumpMsg(msg); 180 // the plain message to be crytographically processed 181 MimeMessage plainMessage = (MimeMessage)msg; 182 183 System.out.println("\n\n*****************************************\n\n"); 184 185 // include RFC822 headers of original message 186 boolean includeHeaders = true; 187 // This is an explicitly signed message 188 msg = createSignedMessage(session, plainMessage, false, includeHeaders); 189 System.out.println("creating explicitly signed message..."); 190 baos.reset(); 191 msg.saveChanges(); 192 msg.writeTo(baos); 193 bais = new ByteArrayInputStream(baos.toByteArray()); 194 msg = new MimeMessage(session, bais); 195 if (PRINT_MESSAGES) { 196 printMessage(msg); 197 } 198 DumpMessage.dumpMsg(msg); 199 200 System.out.println("\n\n*****************************************\n\n"); 201 202 // This is an implicitly signed message 203 msg = createSignedMessage(session, plainMessage, true, includeHeaders); 204 System.out.println("creating implicitly signed message..."); 205 baos.reset(); 206 msg.saveChanges(); 207 msg.writeTo(baos); 208 bais = new ByteArrayInputStream(baos.toByteArray()); 209 msg = new MimeMessage(session, bais); 210 if (PRINT_MESSAGES) { 211 printMessage(msg); 212 } 213 DumpMessage.dumpMsg(msg); 214 215 System.out.println("\n\n*****************************************\n\n"); 216 217 // Now create an encrypted message 218 msg = createEncryptedMessage(session, 219 plainMessage, 220 (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 221 256, 222 includeHeaders); 223 System.out.println("creating encrypted message [AES]..."); 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 // Now create a implicitly signed and encrypted message with attachment 237 System.out.println("creating implicitly signed and encrypted message..."); 238 msg = createSignedAndEncryptedMessage(session, 239 plainMessage, 240 true, 241 includeHeaders); 242 243 baos.reset(); 244 msg.saveChanges(); 245 msg.writeTo(baos); 246 bais = new ByteArrayInputStream(baos.toByteArray()); 247 msg = new MimeMessage(session, bais); 248 if (PRINT_MESSAGES) { 249 printMessage(msg); 250 } 251 DumpMessage.dumpMsg(msg); 252 253 System.out.println("\n\n*****************************************\n\n"); 254 255 // Now create a explicitly signed and encrypted message with attachment 256 System.out.println("creating explicitly signed and encrypted message ..."); 257 msg = createSignedAndEncryptedMessage(session, plainMessage, false, includeHeaders); 258 baos.reset(); 259 msg.saveChanges(); 260 msg.writeTo(baos); 261 bais = new ByteArrayInputStream(baos.toByteArray()); 262 msg = new MimeMessage(session, bais); 263 if (PRINT_MESSAGES) { 264 printMessage(msg); 265 } 266 DumpMessage.dumpMsg(msg); 267 268 System.out.println("\n\n*****************************************\n\n"); 269 270 // compressed message 271 msg = createCompressedMessage(session, 272 plainMessage, 273 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(), 274 includeHeaders); 275 System.out.println("creating message with compressed data..."); 276 baos.reset(); 277 msg.saveChanges(); 278 msg.writeTo(baos); 279 bais = new ByteArrayInputStream(baos.toByteArray()); 280 msg = new MimeMessage(session, bais); 281 if (PRINT_MESSAGES) { 282 printMessage(msg); 283 } 284 DumpMessage.dumpMsg(msg); 285 286 287 // now the same again but do not include RFC822 headers of original message 288 includeHeaders = false; 289 // This is an explicitly signed message 290 msg = createSignedMessage(session, plainMessage, false, includeHeaders); 291 System.out.println("creating explicitly signed message..."); 292 baos.reset(); 293 msg.saveChanges(); 294 msg.writeTo(baos); 295 bais = new ByteArrayInputStream(baos.toByteArray()); 296 msg = new MimeMessage(session, bais); 297 if (PRINT_MESSAGES) { 298 printMessage(msg); 299 } 300 DumpMessage.dumpMsg(msg); 301 302 System.out.println("\n\n*****************************************\n\n"); 303 304 305 // This is an implicitly signed message 306 msg = createSignedMessage(session, plainMessage, true, includeHeaders); 307 System.out.println("creating implicitly signed message..."); 308 baos.reset(); 309 msg.saveChanges(); 310 msg.writeTo(baos); 311 bais = new ByteArrayInputStream(baos.toByteArray()); 312 msg = new MimeMessage(session, bais); 313 if (PRINT_MESSAGES) { 314 printMessage(msg); 315 } 316 DumpMessage.dumpMsg(msg); 317 318 System.out.println("\n\n*****************************************\n\n"); 319 320 // Now create an encrypted message 321 msg = createEncryptedMessage(session, 322 plainMessage, 323 (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 324 256, 325 includeHeaders); 326 System.out.println("creating encrypted message [AES]..."); 327 baos.reset(); 328 msg.saveChanges(); 329 msg.writeTo(baos); 330 bais = new ByteArrayInputStream(baos.toByteArray()); 331 msg = new MimeMessage(session, bais); 332 if (PRINT_MESSAGES) { 333 printMessage(msg); 334 } 335 DumpMessage.dumpMsg(msg); 336 337 System.out.println("\n\n*****************************************\n\n"); 338 339 // Now create a implicitly signed and encrypted message with attachment 340 System.out.println("creating implicitly signed and encrypted message ..."); 341 msg = createSignedAndEncryptedMessage(session, plainMessage, true, includeHeaders); 342 baos.reset(); 343 msg.saveChanges(); 344 msg.writeTo(baos); 345 bais = new ByteArrayInputStream(baos.toByteArray()); 346 msg = new MimeMessage(session, bais); 347 if (PRINT_MESSAGES) { 348 printMessage(msg); 349 } 350 DumpMessage.dumpMsg(msg); 351 352 System.out.println("\n\n*****************************************\n\n"); 353 354 // Now create a explicitly signed and encrypted message with attachment 355 System.out.println("creating explicitly signed and encrypted message..."); 356 msg = createSignedAndEncryptedMessage(session, plainMessage, false, includeHeaders); 357 baos.reset(); 358 msg.saveChanges(); 359 msg.writeTo(baos); 360 bais = new ByteArrayInputStream(baos.toByteArray()); 361 msg = new MimeMessage(session, bais); 362 if (PRINT_MESSAGES) { 363 printMessage(msg); 364 } 365 DumpMessage.dumpMsg(msg); 366 367 System.out.println("\n\n*****************************************\n\n"); 368 369 // compressed message 370 msg = createCompressedMessage(session, 371 plainMessage, 372 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(), 373 includeHeaders); 374 System.out.println("creating message with compressed data..."); 375 baos.reset(); 376 msg.saveChanges(); 377 msg.writeTo(baos); 378 bais = new ByteArrayInputStream(baos.toByteArray()); 379 msg = new MimeMessage(session, bais); 380 if (PRINT_MESSAGES) { 381 printMessage(msg); 382 } 383 DumpMessage.dumpMsg(msg); 384 385 } catch (Exception ex) { 386 ex.printStackTrace(); 387 throw new RuntimeException(ex.toString()); 388 } 389 } 390 391 /** 392 * Creates a MIME message container with the given subject for the given session. 393 * 394 * @param session the mail sesion 395 * @param subject the subject of the message 396 * 397 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content) 398 * 399 * @throws MessagingException if the message cannot be created 400 */ 401 public Message createMessage(Session session, String subject) throws MessagingException { 402 MimeMessage msg = new MimeMessage(session); 403 msg.setFrom(new InternetAddress(from)); 404 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false)); 405 msg.setSentDate(new Date()); 406 msg.setSubject(subject); 407 return msg; 408 } 409 410 /** 411 * Creates a simple plain (neither signed nor encrypted) message. 412 * 413 * @param session the mail session 414 * @param dataHandler the content of the message 415 * 416 * @return the plain message 417 * 418 * @throws MessagingException if an error occurs when creating the message 419 */ 420 public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException { 421 422 Message msg = createMessage(session, "IAIK-S/MIME: Plain message"); 423 if (dataHandler != null) { 424 msg.setDataHandler(dataHandler); 425 } else { 426 msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n"); 427 } 428 return msg; 429 } 430 431 /** 432 * Creates a signed and encrypted message. 433 * 434 * @param session the mail session 435 * @param message the message to be signed and encrypted 436 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 437 * (multipart/signed) signing 438 * @param includeHeaders whether to inlcude the RFC822 headers of the original 439 * message 440 * 441 * @return the signed and encrypted message 442 * 443 * @throws MessagingException if an error occurs when creating the message 444 */ 445 public Message createSignedAndEncryptedMessage(Session session, 446 MimeMessage message, 447 boolean implicit, 448 boolean includeHeaders) 449 throws MessagingException { 450 451 String subject = null; 452 String text = null; 453 if (implicit) { 454 subject = "IAIK-S/MIME: Implicitly Signed and Encrypted"; 455 text = "This message is implicitly signed and encrypted!\n\n\n"; 456 } else { 457 subject = "IAIK-S/MIME: Explicitly Signed and Encrypted"; 458 text = "This message is explicitly signed and encrypted!\n\n\n"; 459 } 460 Message msg = createMessage(session, subject); 461 462 SignedContent sc = new SignedContent(implicit); 463 // set the message content 464 if (includeHeaders) { 465 sc.setContent(message, message.getContentType()); 466 } else { 467 sc.setDataHandler(message.getDataHandler()); 468 } 469 sc.setCertificates(signerCertificates); 470 try { 471 sc.addSigner((RSAPrivateKey)signerPrivateKey, signerCertificate); 472 } catch (NoSuchAlgorithmException ex) { 473 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 474 } 475 476 EncryptedContent ec = new EncryptedContent(sc); 477 // encrypt for the recipient 478 ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 479 // I want to be able to decrypt the message, too 480 ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 481 // set the encryption algorithm 482 try { 483 ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256); 484 } catch (NoSuchAlgorithmException ex) { 485 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 486 } 487 msg.setContent(ec, ec.getContentType()); 488 // let the EncryptedContent update some message headers 489 ec.setHeaders(msg); 490 491 return msg; 492 } 493 494 /** 495 * Creates a signed message. 496 * 497 * @param session the mail session 498 * @param message the message to be signed 499 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 500 * (multipart/signed) signing 501 * @param includeHeaders whether to inlcude the RFC822 headers of the original 502 * message 503 * 504 * @return the signed message 505 * 506 * @throws MessagingException if an error occurs when creating the message 507 */ 508 public Message createSignedMessage(Session session, 509 MimeMessage message, 510 boolean implicit, 511 boolean includeHeaders) 512 throws Exception { 513 514 String subject = null; 515 516 if (implicit) { 517 subject = "IAIK-S/MIME: Implicitly Signed"; 518 } else { 519 subject = "IAIK-S/MIME: Explicitly Signed"; 520 } 521 522 Message msg = createMessage(session, subject); 523 524 SignedContent sc = new SignedContent(implicit); 525 // set message content 526 if (includeHeaders) { 527 sc.setContent(message, message.getContentType()); 528 } else { 529 sc.setDataHandler(message.getDataHandler()); 530 } 531 sc.setCertificates(signerCertificates); 532 533 try { 534 sc.addSigner((RSAPrivateKey)signerPrivateKey, signerCertificate); 535 } catch (NoSuchAlgorithmException ex) { 536 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 537 } 538 539 msg.setContent(sc, sc.getContentType()); 540 // let the SignedContent update some message headers 541 sc.setHeaders(msg); 542 return msg; 543 } 544 545 /** 546 * Creates an encrypted message. 547 * 548 * @param session the mail session 549 * @param message the message to be encrypted 550 * @param algorithm the content encryption algorithm to be used 551 * @param keyLength the length of the secret content encryption key to be created and used 552 * @param includeHeaders whether to inlcude the RFC822 headers of the original 553 * message 554 * 555 * @return the encrypted message 556 * 557 * @throws MessagingException if an error occurs when creating the message 558 */ 559 public Message createEncryptedMessage(Session session, 560 MimeMessage message, 561 AlgorithmID algorithm, 562 int keyLength, 563 boolean includeHeaders) 564 throws MessagingException { 565 566 StringBuffer subject = new StringBuffer(); 567 subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName()); 568 if (keyLength > 0) { 569 subject.append("/"+keyLength); 570 } 571 subject.append("]"); 572 Message msg = createMessage(session, subject.toString()); 573 574 EncryptedContent ec = new EncryptedContent(); 575 // set message content 576 if (includeHeaders) { 577 ec.setContent(message, message.getContentType()); 578 } else { 579 ec.setDataHandler(message.getDataHandler()); 580 } 581 582 // encrypt for the recipient 583 ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 584 // I want to be able to decrypt the message, too 585 ec.addRecipient(encryptionCertOfSigner, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 586 try { 587 ec.setEncryptionAlgorithm(algorithm, keyLength); 588 } catch (NoSuchAlgorithmException ex) { 589 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 590 } 591 592 msg.setContent(ec, ec.getContentType()); 593 // let the EncryptedContent update some message headers 594 ec.setHeaders(msg); 595 596 return msg; 597 } 598 599 600 /** 601 * Creates a compressed message. 602 * 603 * @param session the mail session 604 * @param message the message to be compressed 605 * @param algorithm the compression algorithm to be used 606 * @param includeHeaders whether to inlcude the RFC822 headers of the original 607 * message 608 * 609 * @return the compressed message 610 * 611 * @throws MessagingException if an error occurs when creating the message 612 */ 613 public Message createCompressedMessage(Session session, 614 MimeMessage message, 615 AlgorithmID algorithm, 616 boolean includeHeaders) 617 throws MessagingException { 618 619 String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]"; 620 Message msg = createMessage(session, subject.toString()); 621 622 CompressedContent compressedContent = new CompressedContent(); 623 // set message content 624 if (includeHeaders) { 625 compressedContent.setContent(message, message.getContentType()); 626 } else { 627 compressedContent.setDataHandler(message.getDataHandler()); 628 } 629 630 try { 631 compressedContent.setCompressionAlgorithm(algorithm); 632 } catch (NoSuchAlgorithmException ex) { 633 throw new MessagingException("Compression algorithm not supported: " + ex.getMessage()); 634 } 635 636 msg.setContent(compressedContent, compressedContent.getContentType()); 637 // let the CompressedContent update some message headers 638 compressedContent.setHeaders(msg); 639 640 return msg; 641 } 642 643 644 645 /** 646 * Prints a dump of the given message to System.out. 647 * 648 * @param msg the message to be dumped to System.out 649 */ 650 private static void printMessage(Message msg) throws IOException { 651 System.out.println("------------------------------------------------------------------"); 652 System.out.println("Message dump: \n"); 653 try { 654 msg.writeTo(System.out); 655 } catch (MessagingException ex) { 656 throw new IOException(ex.getMessage()); 657 } 658 System.out.println("\n------------------------------------------------------------------"); 659 } 660 661 662 /** 663 * The main method. 664 */ 665 public static void main(String[] argv) throws IOException { 666 667 DemoSMimeUtil.initDemos(); 668 (new ProcessMessageDemo()).start(); 669 System.out.println("\nReady!"); 670 DemoUtil.waitKey(); 671 } 672}