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/big/BigSMimeMailDemo.java 23 12.02.25 17:59 Dbratko $ 029// $Revision: 23 $ 030// 031 032package demo.smime.big; 033 034import java.io.BufferedInputStream; 035import java.io.BufferedOutputStream; 036import java.io.File; 037import java.io.FileInputStream; 038import java.io.FileOutputStream; 039import java.io.IOException; 040import java.io.InputStream; 041import java.io.OutputStream; 042import java.security.NoSuchAlgorithmException; 043import java.security.PrivateKey; 044import java.security.SignatureException; 045import java.security.interfaces.RSAPrivateKey; 046import java.util.Date; 047import java.util.Random; 048 049import jakarta.activation.DataHandler; 050import jakarta.activation.FileDataSource; 051import jakarta.mail.Message; 052import jakarta.mail.MessagingException; 053import jakarta.mail.Part; 054import jakarta.mail.Session; 055import jakarta.mail.internet.InternetAddress; 056import jakarta.mail.internet.MimeMessage; 057 058import demo.DemoSMimeUtil; 059import demo.DemoUtil; 060import demo.keystore.CMSKeyStore; 061import demo.smime.DumpMessage; 062import iaik.asn1.structures.AlgorithmID; 063import iaik.cms.CMSAlgorithmID; 064import iaik.smime.AuthEncryptedContent; 065import iaik.smime.CompressedContent; 066import iaik.smime.EncryptedContent; 067import iaik.smime.SMimeParameters; 068import iaik.smime.SharedFileInputStream; 069import iaik.smime.SignedContent; 070import iaik.utils.CryptoUtils; 071import iaik.utils.Util; 072import iaik.x509.X509Certificate; 073 074/** 075 * This class demonstrates the usage of the IAIK S/MIME implementation for 076 * handling S/MIME message with big content data. 077 * <p> 078 * The only difference to the common usage of this S/MIME library is that 079 * this demo uses a temporary file directory to which the content of the 080 * big messages is written during processing. The temporary directory is 081 * created by calling method {@link iaik.smime.SMimeParameters#setTempDirectory(String, int) 082 * SMimeParameters#setTempDirectory}: 083 * <pre> 084 * int bufSize = 16348; 085 * String tmpDir = ...; 086 * SMimeParameters.setTempDirectory(tmpDir, bufSize); 087 * </pre> 088 * See Javadoc of {@link iaik.smime.SMimeParameters#setTempDirectory(String, int) 089 * SMimeParameters#setTempDirectory} for usage information. 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 * <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 101 * </li> 102 * <li> 103 * <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a> 104 * </li> 105 * </ul> 106 * The data for this demo is randomly created and stored into a file which is 107 * deleted again at the end of this demo. Note that running this demo may take 108 * some certain time because it processes some MB of data. 109 */ 110public class BigSMimeMailDemo { 111 112 // The directory where to write mails. 113 final static String TEST_DIR = "test"; 114 115 // The name of the data file. 116 final static String DATA_FILE_NAME = TEST_DIR + "/test.dat"; 117 118 // The data size (in bytes). 119 final static int DATA_SIZE = 15000 * 1024; 120 121 122 String firstName_ = "John"; // name of sender 123 String lastName_ = "SMime"; 124 String from_ = "smimetest@iaik.tugraz.at"; // email sender 125 String to_ = "smimetest@iaik.tugraz.at"; // email recipient 126 String host_ = "mailhost"; // name of the mailhost 127 128 X509Certificate[] signerCertificates_; // list of certificates to include in the S/MIME message 129 X509Certificate recipientCertificate_; // certificate of the recipient 130 PrivateKey recipientKey_; // the private key of the recipient 131 X509Certificate signerCertificate_; // certificate of the signer/sender 132 X509Certificate encryptionCertOfSigner_; // signer uses different certificate for encryption 133 PrivateKey signerPrivateKey_; // private key of the signer/sender 134 135 136 /** 137 * Default constructor. Reads certificates and keys from the demo keystore. 138 */ 139 public BigSMimeMailDemo() { 140 141 System.out.println(); 142 System.out.println("******************************************************************************************"); 143 System.out.println("* BigSMimeMailDemo demo *"); 144 System.out.println("* (shows how to create and parse big S/MIME messages) *"); 145 System.out.println("******************************************************************************************"); 146 System.out.println(); 147 148 // get the certificates from the KeyStore 149 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 150 signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 151 signerCertificate_ = signerCertificates_[0]; 152 153 // recipient = signer for this test 154 recipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 155 recipientKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 156 encryptionCertOfSigner_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 157 } 158 159 /** 160 * Starts the demo. 161 * 162 * @throws IOException if an I/O related error occurs 163 */ 164 public void start() throws IOException { 165 166 // first we create the big test data file (may take some time 167 createDataFile(DATA_FILE_NAME, DATA_SIZE); 168 169 // get the default Session 170 Session session = DemoSMimeUtil.getSession(); 171 172 // the name of the file holding the test message 173 String fileName; 174 // the file holding the test message 175 File file; 176 // the stream to which to write the mail to a file 177 FileOutputStream fos = null; 178 // the stream from which to read the mail from a file 179 SharedFileInputStream fis = null; 180 181 // we specify a temp directory to which temporary message contents 182 // shall be written 183 int bufSize = 64 * 1024; 184 SMimeParameters.setTempDirectory(TEST_DIR, bufSize); 185 186 try { 187 188 // DataHandler for reading the data 189 DataHandler dataHandler = new DataHandler(new FileDataSource(DATA_FILE_NAME)); 190 191 Message msg; // the message to send 192 193 // 1. Explicitly signed message 194 System.out.println("Creating explicitly signed message..."); 195 // create 196 msg = createSignedMessage(session, dataHandler, false); 197 msg.saveChanges(); 198 fileName = TEST_DIR + "/explicitSigned.eml"; 199 fos = new FileOutputStream(fileName); 200 System.out.println("Writing explicitly signed message to " + fileName); 201 msg.writeTo(new BufferedOutputStream(fos)); 202 fos.close(); 203 System.out.println("Explicitly signed message created."); 204 // read 205 file = new File(fileName); 206 fis = new SharedFileInputStream(file); 207 System.out.println("Parsing explicitly signed message from " + fileName); 208 msg = new MimeMessage(session, fis); 209 parse(msg); 210 fis.close(); 211 file.delete(); 212 System.out.println("\n\n*****************************************\n\n"); 213 214 215 // 2. Implicitly signed message 216 msg = createSignedMessage(session, dataHandler, true); 217 System.out.println("creating implicitly signed message..."); 218 fileName = TEST_DIR + "/implicitSigned.eml"; 219 fos = new FileOutputStream(fileName); 220 System.out.println("Writing implicitly signed message to " + fileName); 221 msg.writeTo(new BufferedOutputStream(fos)); 222 fos.close(); 223 System.out.println("Implicitly signed message created."); 224 // read 225 file = new File(fileName); 226 fis = new SharedFileInputStream(file); 227 System.out.println("Parsing implicitly signed message from " + fileName); 228 msg = new MimeMessage(session, fis); 229 parse(msg); 230 fis.close(); 231 file.delete(); 232 System.out.println("\n\n*****************************************\n\n"); 233 234 // 3. Encrypted messages (AES) 235 System.out.println("Creating encrypted message [AES/256]..."); 236 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, false); 237 fileName = TEST_DIR + "/encrypted.eml"; 238 fos = new FileOutputStream(fileName); 239 System.out.println("Writing encrypted message to " + fileName); 240 msg.writeTo(new BufferedOutputStream(fos)); 241 fos.close(); 242 System.out.println("Encrypted message created."); 243 // read 244 file = new File(fileName); 245 fis = new SharedFileInputStream(file); 246 System.out.println("Parsing encrypted message from " + fileName); 247 msg = new MimeMessage(session, fis); 248 parse(msg); 249 fis.close(); 250 file.delete(); 251 252 System.out.println("\n\n*****************************************\n\n"); 253 254 // 4. Implicitly signed and encrypted message 255 System.out.println("Creating implicitly signed and encrypted message"); 256 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, true, false); 257 fileName = TEST_DIR + "/impsigenc.eml"; 258 fos = new FileOutputStream(fileName); 259 System.out.println("Writing implicitly signed and encrypted message to " + fileName); 260 msg.writeTo(new BufferedOutputStream(fos)); 261 fos.close(); 262 System.out.println("Implicitly signed and encrypted message created."); 263 // read 264 file = new File(fileName); 265 fis = new SharedFileInputStream(file); 266 System.out.println("Parsing implicitly signed and encrypted message from " + fileName); 267 msg = new MimeMessage(session, fis); 268 parse(msg); 269 fis.close(); 270 file.delete(); 271 272 System.out.println("\n\n*****************************************\n\n"); 273 274 // 6. Explicitly signed and encrypted message 275 System.out.println("Creating explicitly signed and encrypted message"); 276 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256, false, false); 277 fileName = TEST_DIR + "/impsigenc.eml"; 278 fos = new FileOutputStream(fileName); 279 System.out.println("Writing explicitly signed and encrypted message to " + fileName); 280 msg.writeTo(new BufferedOutputStream(fos)); 281 fos.close(); 282 System.out.println("Explicitly signed and encrypted message created."); 283 // read 284 file = new File(fileName); 285 fis = new SharedFileInputStream(file); 286 System.out.println("Parsing explicitly signed and encrypted message from " + fileName); 287 msg = new MimeMessage(session, fis); 288 parse(msg); 289 fis.close(); 290 file.delete(); 291 292 System.out.println("\n\n*****************************************\n\n"); 293 294 // 7. compressed message 295 System.out.println("Creating compressed message"); 296 msg = createCompressedMessage(session, dataHandler, (AlgorithmID)CMSAlgorithmID.zlib_compress.clone()); 297 fileName = TEST_DIR + "/compressed.eml"; 298 System.out.println("Writing compressed message to " + fileName); 299 fos = new FileOutputStream(fileName); 300 msg.writeTo(new BufferedOutputStream(fos)); 301 fos.close(); 302 System.out.println("Compressed message created."); 303 // read 304 file = new File(fileName); 305 fis = new SharedFileInputStream(file); 306 System.out.println("Parsing compressed message from " + fileName); 307 msg = new MimeMessage(session, fis); 308 parse(msg); 309 fis.close(); 310 file.delete(); 311 312 // 8. Authenticated Encrypted messages (AES) 313 System.out.println("Creating authenticated encrypted message [AES-GCM/256]..."); 314 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, true); 315 fileName = TEST_DIR + "/encrypted.eml"; 316 fos = new FileOutputStream(fileName); 317 System.out.println("Writing encrypted message to " + fileName); 318 msg.writeTo(new BufferedOutputStream(fos)); 319 fos.close(); 320 System.out.println("Encrypted message created."); 321 // read 322 file = new File(fileName); 323 fis = new SharedFileInputStream(file); 324 System.out.println("Parsing encrypted message from " + fileName); 325 msg = new MimeMessage(session, fis); 326 parse(msg); 327 fis.close(); 328 file.delete(); 329 330 System.out.println("\n\n*****************************************\n\n"); 331 332 333 // 9. Authenticated Encrypted messages (ChaCha20Poly1305) 334 System.out.println("Creating authenticated encrypted message [ChaCha20Poly1305]..."); 335 msg = createEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, true); 336 fileName = TEST_DIR + "/encrypted.eml"; 337 fos = new FileOutputStream(fileName); 338 System.out.println("Writing encrypted message to " + fileName); 339 msg.writeTo(new BufferedOutputStream(fos)); 340 fos.close(); 341 System.out.println("Encrypted message created."); 342 // read 343 file = new File(fileName); 344 fis = new SharedFileInputStream(file); 345 System.out.println("Parsing encrypted message from " + fileName); 346 msg = new MimeMessage(session, fis); 347 parse(msg); 348 fis.close(); 349 file.delete(); 350 351 System.out.println("\n\n*****************************************\n\n"); 352 353 // 10. Implicitly signed and authenticated encrypted message 354 System.out.println("Creating implicitly signed and authenticated encrypted message (AES)"); 355 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, true, true); 356 fileName = TEST_DIR + "/impsigenc.eml"; 357 fos = new FileOutputStream(fileName); 358 System.out.println("Writing implicitly signed and encrypted message to " + fileName); 359 msg.writeTo(new BufferedOutputStream(fos)); 360 fos.close(); 361 System.out.println("Implicitly signed and encrypted message created."); 362 // read 363 file = new File(fileName); 364 fis = new SharedFileInputStream(file); 365 System.out.println("Parsing implicitly signed and encrypted message from " + fileName); 366 msg = new MimeMessage(session, fis); 367 parse(msg); 368 fis.close(); 369 file.delete(); 370 371 System.out.println("\n\n*****************************************\n\n"); 372 373 // 11. Explicitly signed and authenticated encrypted message 374 System.out.println("Creating explicitly authenticated signed and encrypted message (AES)"); 375 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.aes256_GCM.clone(), 256, false, true); 376 fileName = TEST_DIR + "/impsigenc.eml"; 377 fos = new FileOutputStream(fileName); 378 System.out.println("Writing explicitly signed and encrypted message to " + fileName); 379 msg.writeTo(new BufferedOutputStream(fos)); 380 fos.close(); 381 System.out.println("Explicitly signed and encrypted message created."); 382 // read 383 file = new File(fileName); 384 fis = new SharedFileInputStream(file); 385 System.out.println("Parsing explicitly signed and encrypted message from " + fileName); 386 msg = new MimeMessage(session, fis); 387 parse(msg); 388 fis.close(); 389 file.delete(); 390 391 System.out.println("\n\n*****************************************\n\n"); 392 393 // 12. Implicitly signed and authenticated encrypted message 394 System.out.println("Creating implicitly signed and authenticated encrypted message (ChaCha20Poly1305)"); 395 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, true, true); 396 fileName = TEST_DIR + "/impsigenc.eml"; 397 fos = new FileOutputStream(fileName); 398 System.out.println("Writing implicitly signed and encrypted message to " + fileName); 399 msg.writeTo(new BufferedOutputStream(fos)); 400 fos.close(); 401 System.out.println("Implicitly signed and encrypted message created."); 402 // read 403 file = new File(fileName); 404 fis = new SharedFileInputStream(file); 405 System.out.println("Parsing implicitly signed and encrypted message from " + fileName); 406 msg = new MimeMessage(session, fis); 407 parse(msg); 408 fis.close(); 409 file.delete(); 410 411 System.out.println("\n\n*****************************************\n\n"); 412 413 // 13. Explicitly signed and authenticated encrypted message 414 System.out.println("Creating explicitly authenticated signed and encrypted message (ChaCha20Poly1305)"); 415 msg = createSignedAndEncryptedMessage(session, dataHandler, (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(), 256, false, true); 416 fileName = TEST_DIR + "/impsigenc.eml"; 417 fos = new FileOutputStream(fileName); 418 System.out.println("Writing explicitly signed and encrypted message to " + fileName); 419 msg.writeTo(new BufferedOutputStream(fos)); 420 fos.close(); 421 System.out.println("Explicitly signed and encrypted message created."); 422 // read 423 file = new File(fileName); 424 fis = new SharedFileInputStream(file); 425 System.out.println("Parsing explicitly signed and encrypted message from " + fileName); 426 msg = new MimeMessage(session, fis); 427 parse(msg); 428 fis.close(); 429 file.delete(); 430 431 System.out.println("\n\n*****************************************\n\n"); 432 433 434 } catch (Exception ex) { 435 if (fos != null) { 436 try { 437 fos.close(); 438 } catch (Exception e) { 439 // ignore 440 } 441 } 442 if (fis != null) { 443 try { 444 fis.close(); 445 } catch (Exception e) { 446 // ignore 447 } 448 } 449 ex.printStackTrace(); 450 throw new RuntimeException(ex.toString()); 451 452 } finally { 453 // try to delete temporary directory 454 SMimeParameters.deleteTempDirectory(); 455 SMimeParameters.setTempDirectory(null, -1); 456 // delete data file 457 File dataFile = new File(DATA_FILE_NAME); 458 dataFile.delete(); 459 } 460 461 } 462 463 464 /** 465 * Creates a MIME message container with the given subject for the given session. 466 * 467 * @param session the mail sesion 468 * @param subject the subject of the message 469 * 470 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content) 471 * 472 * @throws MessagingException if the message cannot be created 473 */ 474 public Message createMessage(Session session, String subject) throws MessagingException { 475 MimeMessage msg = new MimeMessage(session); 476 msg.setFrom(new InternetAddress(from_)); 477 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to_, false)); 478 msg.setSentDate(new Date()); 479 msg.setSubject(subject); 480 return msg; 481 } 482 483 484 /** 485 * Creates a signed and encrypted message. 486 * 487 * @param session the mail session 488 * @param dataHandler the content of the message to be signed and encrypted 489 * @param algorithm the content encryption algorithm to be used 490 * @param keyLength the length of the secret content encryption key to be created and used 491 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 492 * (multipart/signed) signing 493 * @param authEncrypt whether to create an authenticated encrypted or an encrypted message 494 * 495 * @return the signed and encrypted message 496 * 497 * @throws MessagingException if an error occurs when creating the message 498 */ 499 public Message createSignedAndEncryptedMessage(Session session, 500 DataHandler dataHandler, 501 AlgorithmID algorithm, 502 int keyLength, 503 boolean implicit, 504 boolean authEncrypt) 505 throws MessagingException { 506 507 String subject = null; 508 if (implicit) { 509 subject = "IAIK-S/MIME: Implicitly Signed and Encrypted"; 510 } else { 511 subject = "IAIK-S/MIME: Explicitly Signed and Encrypted"; 512 } 513 Message msg = createMessage(session, subject); 514 515 SignedContent sc = new SignedContent(implicit); 516 sc.setDataHandler(dataHandler); 517 sc.setCertificates(signerCertificates_); 518 try { 519 sc.addSigner((RSAPrivateKey)signerPrivateKey_, signerCertificate_); 520 } catch (NoSuchAlgorithmException ex) { 521 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 522 } 523 524 EncryptedContent ec = authEncrypt ? new AuthEncryptedContent(sc) : new EncryptedContent(sc); 525 // encrypt for the recipient 526 ec.addRecipient(recipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 527 // I want to be able to decrypt the message, too 528 ec.addRecipient(encryptionCertOfSigner_, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 529 // set the encryption algorithm 530 try { 531 ec.setEncryptionAlgorithm((AlgorithmID)algorithm.clone(), keyLength); 532 } catch (NoSuchAlgorithmException ex) { 533 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 534 } 535 msg.setContent(ec, ec.getContentType()); 536 // let the EncryptedContent update some message headers 537 ec.setHeaders(msg); 538 539 return msg; 540 } 541 542 /** 543 * Creates a signed message. 544 * 545 * @param session the mail session 546 * @param dataHandler the content of the message to be signed 547 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 548 * (multipart/signed) signing 549 * 550 * @return the signed message 551 * 552 * @throws MessagingException if an error occurs when creating the message 553 */ 554 public Message createSignedMessage(Session session, DataHandler dataHandler, boolean implicit) 555 throws MessagingException { 556 557 String subject = null; 558 StringBuffer buf = new StringBuffer(); 559 560 if (implicit) { 561 subject = "IAIK-S/MIME: Implicitly Signed"; 562 buf.append("This message is implicitly signed!\n"); 563 buf.append("You need an S/MIME aware mail client to view this message.\n"); 564 buf.append("\n\n"); 565 } else { 566 subject = "IAIK-S/MIME: Explicitly Signed"; 567 buf.append("This message is explicitly signed!\n"); 568 buf.append("Every mail client can view this message.\n"); 569 buf.append("Non S/MIME mail clients will show the signature as attachment.\n"); 570 buf.append("\n\n"); 571 } 572 573 Message msg = createMessage(session, subject); 574 575 SignedContent sc = new SignedContent(implicit); 576 577 if (dataHandler != null) { 578 sc.setDataHandler(dataHandler); 579 } else { 580 sc.setText(buf.toString()); 581 } 582 sc.setCertificates(signerCertificates_); 583 584 try { 585 sc.addSigner((RSAPrivateKey)signerPrivateKey_, signerCertificate_); 586 } catch (NoSuchAlgorithmException ex) { 587 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 588 } 589 590 msg.setContent(sc, sc.getContentType()); 591 // let the SignedContent update some message headers 592 sc.setHeaders(msg); 593 return msg; 594 } 595 596 /** 597 * Creates an encrypted message. 598 * 599 * @param session the mail session 600 * @param dataHandler the dataHandler providing the content to be encrypted 601 * @param algorithm the content encryption algorithm to be used 602 * @param keyLength the length of the secret content encryption key to be created and used 603 * @param authEncrypt whether to create an authenticated encrypted or an encrypted message 604 * 605 * @return the encrypted message 606 * 607 * @throws MessagingException if an error occurs when creating the message 608 */ 609 public Message createEncryptedMessage(Session session, 610 DataHandler dataHandler, 611 AlgorithmID algorithm, 612 int keyLength, 613 boolean authEncrypt) 614 throws MessagingException { 615 616 StringBuffer subject = new StringBuffer(); 617 subject.append("IAIK-S/MIME: " + (authEncrypt ? "Authenticated " : "") + "Encrypted ["+algorithm.getName()); 618 if (keyLength > 0) { 619 subject.append("/"+keyLength); 620 } 621 subject.append("]"); 622 Message msg = createMessage(session, subject.toString()); 623 624 EncryptedContent ec = authEncrypt ? new AuthEncryptedContent() : new EncryptedContent(); 625 626 627 ec.setDataHandler(dataHandler); 628 // encrypt for the recipient 629 ec.addRecipient(recipientCertificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 630 // I want to be able to decrypt the message, too 631 ec.addRecipient(encryptionCertOfSigner_, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 632 try { 633 ec.setEncryptionAlgorithm(algorithm, keyLength); 634 } catch (NoSuchAlgorithmException ex) { 635 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 636 } 637 638 msg.setContent(ec, ec.getContentType()); 639 // let the EncryptedContent update some message headers 640 ec.setHeaders(msg); 641 642 return msg; 643 } 644 645 /** 646 * Creates a compressed message. 647 * 648 * @param session the mail session 649 * @param dataHandler the datahandler supplying the content to be compressed 650 * @param algorithm the compression algorithm to be used 651 * 652 * @return the compressed message 653 * 654 * @throws MessagingException if an error occurs when creating the message 655 */ 656 public Message createCompressedMessage(Session session, DataHandler dataHandler, AlgorithmID algorithm) 657 throws MessagingException { 658 659 String subject = "IAIK-S/MIME: Compressed ["+algorithm.getName()+"]"; 660 Message msg = createMessage(session, subject.toString()); 661 662 CompressedContent compressedContent = new CompressedContent(); 663 compressedContent.setDataHandler(dataHandler); 664 665 try { 666 compressedContent.setCompressionAlgorithm(algorithm); 667 } catch (NoSuchAlgorithmException ex) { 668 throw new MessagingException("Compression algorithm not supported: " + ex.getMessage()); 669 } 670 671 msg.setContent(compressedContent, compressedContent.getContentType()); 672 // let the CompressedContent update some message headers 673 compressedContent.setHeaders(msg); 674 675 return msg; 676 } 677 678 /** 679 * Parses the given object (message, part, ... ). 680 * 681 * @param o the object (message, part, ... ) to be parsed 682 * 683 * @throws Exception if an exception occurs while parsing 684 */ 685 public void parse(Object o) throws Exception { 686 DumpMessage dumpMsg = new DumpMessage(); 687 if (o instanceof Message) { 688 dumpMsg.dumpEnvelope((Message)o); 689 } 690 if (o instanceof Part) { 691 System.out.println("CONTENT-TYPE: "+((Part)o).getContentType()); 692 o = ((Part)o).getContent(); 693 } 694 695 if (o instanceof EncryptedContent) { 696 697 // encrypted 698 System.out.println("This message is encrypted!"); 699 700 EncryptedContent ec = (EncryptedContent)o; 701 ec.decryptSymmetricKey(recipientKey_, recipientCertificate_); 702 parse(ec.getContent()); 703 704 } else if (o instanceof SignedContent) { 705 706 // signed 707 System.out.println("This message is signed!"); 708 709 SignedContent sc = (SignedContent)o; 710 711 X509Certificate signer = null; 712 try { 713 signer = sc.verify(); 714 System.out.println("This message is signed from: "+signer.getSubjectDN()); 715 } catch (SignatureException ex) { 716 throw new SignatureException("Signature verification error: " + ex.toString()); 717 } 718 719 parse(sc.getContent()); 720 721 } else if (o instanceof CompressedContent) { 722 723 System.out.println("The content of this message is compressed."); 724 CompressedContent compressed = (CompressedContent)o; 725 parse(compressed.getContent()); 726 727 } else if (o instanceof InputStream) { 728 729 // we already know that the content is an input stream (thus we must not check for other content values) 730 System.out.println("Content is just an input stream."); 731 System.out.println("---------------------------"); 732 InputStream is = (InputStream)o; 733 // read content 734 if (readContent(is) == false) { 735 throw new Exception("Content not equal to original one!"); 736 } 737 738 } else { 739 throw new Exception("Unexpected object!"); 740 } 741 } 742 743 744 /** 745 * Reads the content from the given input stream, writes it 746 * to a temp file and then compares the tmp file with the 747 * original data file. 748 * 749 * @param content the content to be written 750 * 751 * @return <code>true</code> if the content is equal to the original one, 752 * <code>false</code> if it differs from the original one 753 * @throws IOException 754 */ 755 private final static boolean readContent(InputStream content) throws IOException { 756 String fileName = TEST_DIR + "/tmp.dat"; 757 758 // file stream to which to write content 759 FileOutputStream fos = null; 760 // file stream from which to read content for comparison 761 FileInputStream fis = null; 762 // temp file to which write / from which read content for comparison 763 File file = null; 764 // file stream from which to read original content for comparison 765 FileInputStream origFis = null; 766 try { 767 // write to file 768 System.out.println("Write content to " + fileName); 769 fos = new FileOutputStream(fileName); 770 InputStream in = new BufferedInputStream(content); 771 OutputStream out = new BufferedOutputStream(fos); 772 Util.copyStream(in, 773 out, 774 new byte[8192]); 775 out.flush(); 776 fos.close(); 777 fos = null; 778 779 // compare contents 780 file = new File(fileName); 781 fis = new FileInputStream(file); 782 origFis = new FileInputStream(DATA_FILE_NAME); 783 System.out.println("Compare with original content"); 784 785 boolean equal = equals(new BufferedInputStream(fis), new BufferedInputStream(origFis)); 786 if (equal) { 787 System.out.println("Content ok"); 788 } 789 return equal; 790 } finally { 791 if (origFis != null) { 792 try { 793 origFis.close(); 794 } catch (IOException ex) { 795 // ignore 796 } 797 } 798 if (fis != null) { 799 try { 800 fis.close(); 801 } catch (IOException ex) { 802 // ignore 803 } 804 // delete tmp file 805 try { 806 file.delete(); 807 } catch (Exception ex) { 808 // ignore 809 } 810 } 811 if (fos != null) { 812 try { 813 fos.close(); 814 } catch (IOException ex) { 815 // ignore 816 } 817 } 818 819 } 820 } 821 822 823 824 825 /** 826 * Creates a file of the given size and fills it with random 827 * data. The file is written to the directory used by this 828 * demo. If the directory does not exist it is created. 829 * 830 * @param fileName the name of the file to be created 831 * @param size the size (in bytes) of the file 832 * 833 * @throws IOException if an exception occurs during creating/writing the 834 * file 835 */ 836 private final static void createDataFile(String fileName, int size) 837 throws IOException { 838 // create output directory 839 File dir = new File(TEST_DIR); 840 if (dir.exists() == false) { 841 dir.mkdir(); 842 } 843 // create big data file 844 System.out.println("Creating " + size + " b data file " + fileName); 845 OutputStream os = null; 846 try { 847 Random random = new Random(); 848 os = new BufferedOutputStream(new FileOutputStream(fileName)); 849 int bufSize = 8192; 850 byte[] buf = new byte[bufSize]; 851 int blockSize = size / bufSize; 852 for (int i = 0; i < blockSize; i++) { 853 random.nextBytes(buf); 854 os.write(buf); 855 os.flush(); 856 } 857 // write the rest 858 buf = new byte[size - blockSize * bufSize]; 859 random.nextBytes(buf); 860 os.write(buf); 861 os.flush(); 862 System.out.println("Data file " + fileName + " created."); 863 } finally { 864 if (os != null) { 865 try { 866 os.close(); 867 } catch (IOException ex) { 868 // ignore 869 } 870 } 871 } 872 } 873 874 /** 875 * Compares the data read from the two input streams. 876 * 877 * @param source1 the first input stream 878 * @param source2 the second input stream 879 * 880 * @return true if the data is equal, false if it is not equal 881 * 882 * @throws IOException if an exception occurs 883 */ 884 private final static boolean equals(InputStream source1, InputStream source2) 885 throws IOException 886 { 887 888 boolean equals; 889 int bufSize = 8192; 890 if (source1 != source2) { 891 if ((source1 != null) && (source2 != null)) { 892 byte[] buffer1 = new byte[bufSize]; 893 byte[] buffer2 = new byte[bufSize]; 894 if (buffer1.length > buffer2.length) { 895 // swap 896 byte[] temp = buffer1; 897 buffer1 = buffer2; 898 buffer2 = temp; 899 } 900 901 equals = true; 902 int bytesRead1; 903 while ((bytesRead1 = source1.read(buffer1)) >= 0) { 904 int bytesRead2; 905 int totalBytesRead2 = 0; 906 while (((bytesRead2 = source2.read(buffer2, totalBytesRead2, (bytesRead1 - totalBytesRead2))) >= 0) 907 && (totalBytesRead2 < bytesRead1)) { 908 totalBytesRead2 += bytesRead2; 909 } 910 if (totalBytesRead2 == bytesRead1) { 911 if (!CryptoUtils.equalsBlock(buffer1, 0, buffer2, 0, bytesRead1)) { 912 equals = false; 913 break; 914 } 915 } else { 916 equals = false; 917 break; 918 } 919 } 920 if (source2.read(buffer2) >= 0) { 921 // there has been data left in stream 2 922 equals = false; 923 } 924 } else { 925 equals = false; 926 } 927 } else { 928 equals = true; 929 } 930 931 return equals ; 932 } 933 934 935 936 /** 937 * The main method. 938 */ 939 public static void main(String[] argv) throws IOException { 940 941 DemoSMimeUtil.initDemos(); 942 (new BigSMimeMailDemo()).start(); 943 System.out.println("\nReady!"); 944 DemoUtil.waitKey(); 945 } 946}