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