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/ecc/SMimeV4EccDemo.java 10 12.02.25 17:59 Dbratko $ 059 // $Revision: 10 $ 060 // 061 062 package demo.smime.ecc; 063 064 import java.io.ByteArrayInputStream; 065 import java.io.ByteArrayOutputStream; 066 import java.io.IOException; 067 import java.security.NoSuchAlgorithmException; 068 import java.security.PrivateKey; 069 import java.util.Date; 070 071 import javax.activation.DataHandler; 072 import javax.activation.FileDataSource; 073 import javax.mail.Message; 074 import javax.mail.MessagingException; 075 import javax.mail.Multipart; 076 import javax.mail.Session; 077 import javax.mail.internet.InternetAddress; 078 import javax.mail.internet.MimeBodyPart; 079 import javax.mail.internet.MimeMessage; 080 081 import demo.DemoSMimeUtil; 082 import demo.DemoUtil; 083 import demo.cms.ecc.ECCDemoUtil; 084 import demo.cms.ecc.keystore.CMSEccKeyStore; 085 import demo.smime.DumpMessage; 086 import iaik.asn1.structures.AlgorithmID; 087 import iaik.cms.CMSAlgorithmID; 088 import iaik.smime.AuthEncryptedContent; 089 import iaik.smime.EncryptedContent; 090 import iaik.smime.SMimeBodyPart; 091 import iaik.smime.SMimeException; 092 import iaik.smime.SMimeMultipart; 093 import iaik.smime.SignedContent; 094 import iaik.utils.KeyAndCertificate; 095 import iaik.x509.X509Certificate; 096 097 /** 098 * This class demonstrates the usage of the IAIK S/MIME implementation. It shows how to create 099 * signed and/or (authenticated) encrypted S/MIMEv4 messages using ECC keys and how to parse them and verify the signatures 100 * and decrypt the content, respectively. 101 * <p> 102 * This demo uses several combinations of cryptographic algorithms that may not used in this 103 * way in practice. For a simple demos using only one set of algorithms see the {@link SimpleSMimeV4EcDemo SimpleSMimeV4EcDemo} 104 * and {@link SimpleSMimeV4EdDemo SimpleSMimeV4EdDemo}. 105 * <p> 106 * Additionally to <code>iaik_cms.jar</code> you also must have 107 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href = 108 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 109 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>), 110 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href = 111 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank"> 112 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>) 113 * in your classpath. 114 * <p> 115 * To run this demo the following packages are required: 116 * <ul> 117 * <li> 118 * <code>iaik_cms.jar</code> 119 * </li> 120 * <li> 121 * <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>). 122 * </li> 123 * <li> 124 * <code>iaik_eccelerate.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">IAIK ECC Library</a>). 125 * </li> 126 * <li> 127 * <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>). 128 * </li> 129 * <li> 130 * <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). 131 * </li> 132 * </ul> 133 * 134 */ 135 public class SMimeV4EccDemo { 136 137 // whether to print dump all generates test messages to System.out 138 final static boolean PRINT_MESSAGES = false; 139 140 /** 141 * Cretaes a Multipart for the demo messages. 142 * 143 * @return the Multipart 144 * 145 * @throws MessagingException if an error occurs when creating the Mulitpart 146 */ 147 static DataHandler createMultipart() throws MessagingException { 148 // Create a demo Multipart 149 MimeBodyPart mbp1 = new SMimeBodyPart(); 150 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n"); 151 // attachment 152 MimeBodyPart attachment = new SMimeBodyPart(); 153 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html"))); 154 attachment.setFileName("test.html"); 155 156 Multipart mp = new SMimeMultipart(); 157 mp.addBodyPart(mbp1); 158 mp.addBodyPart(attachment); 159 DataHandler multipart = new DataHandler(mp, mp.getContentType()); 160 return multipart; 161 } 162 163 164 String firstName_ = "John"; 165 String lastName_ = "SMime"; 166 String to_ = "smimetest@iaik.tugraz.at"; // email recipient 167 String from_ = "smimetest@iaik.tugraz.at"; // email sender 168 String host_ = "mailhost"; // name of the mailhost 169 170 /** 171 * Default constructor. 172 */ 173 public SMimeV4EccDemo() { 174 175 System.out.println(); 176 System.out.println("********************************************************************************************"); 177 System.out.println("* SMimeV4EccDemo demo *"); 178 System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIMEv4 messages) *"); 179 System.out.println("********************************************************************************************"); 180 System.out.println(); 181 182 } 183 184 185 /** 186 * Starts the demo. 187 * 188 * @throws Exception if an error occurs 189 */ 190 public void start() throws Exception { 191 192 // get the default Session 193 Session session = DemoSMimeUtil.getSession(); 194 195 // Create a demo Multipart 196 DataHandler multipart = createMultipart(); 197 198 // get signer key and certs 199 KeyAndCertificate[] signerKeyAndCerts = { 200 // P-256 201 new KeyAndCertificate( 202 CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, 203 CMSEccKeyStore.SZ_256_SIGN), 204 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, 205 CMSEccKeyStore.SZ_256_SIGN)), 206 // P-384 207 new KeyAndCertificate( 208 CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, 209 CMSEccKeyStore.SZ_384_SIGN), 210 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, 211 CMSEccKeyStore.SZ_384_SIGN)), 212 // P-521 213 new KeyAndCertificate( 214 CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, 215 CMSEccKeyStore.SZ_521_SIGN), 216 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, 217 CMSEccKeyStore.SZ_521_SIGN)), 218 // ed25519 219 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519), 220 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519)), 221 // ed448 222 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448), 223 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448)), 224 225 }; 226 227 // the digest and signature algorithms to be used 228 AlgorithmID[][] digestAndSignatureAlgorithms = new AlgorithmID[][] { 229 { CMSAlgorithmID.sha256, CMSAlgorithmID.ecdsa_With_SHA256 }, 230 { CMSAlgorithmID.sha384, CMSAlgorithmID.ecdsa_With_SHA384 }, 231 { CMSAlgorithmID.sha512, CMSAlgorithmID.ecdsa_With_SHA512 }, 232 { CMSAlgorithmID.sha512, CMSAlgorithmID.ed25519}, 233 { CMSAlgorithmID.shake256Len, CMSAlgorithmID.ed448 }, 234 }; 235 236 237 /**************************************************************************/ 238 /* */ 239 /* Signing Demo */ 240 /* */ 241 /**************************************************************************/ 242 243 final int DIGEST_ALG = 0; 244 final int SIGNATURE_ALG = 1; 245 for (int i = 0; i < digestAndSignatureAlgorithms.length; i++) { 246 AlgorithmID digestAlg = digestAndSignatureAlgorithms[i][DIGEST_ALG]; 247 AlgorithmID signatureAlg = digestAndSignatureAlgorithms[i][SIGNATURE_ALG]; 248 PrivateKey signerKey = signerKeyAndCerts[i].getPrivateKey(); 249 X509Certificate[] signerCerts = signerKeyAndCerts[i].getCertificateChain(); 250 System.out.println("Running signing demo for " + signatureAlg.getName()); 251 startSigningDemo(session, 252 multipart, 253 digestAlg, 254 signatureAlg, 255 signerKey, 256 signerCerts); 257 } 258 259 // (authenticated) encryption demos 260 261 // get recipient certs 262 X509Certificate[] recipientCerts = { 263 // P-256 264 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 265 CMSEccKeyStore.SZ_256_CRYPT_1)[0], 266 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 267 CMSEccKeyStore.SZ_256_CRYPT_2)[0], 268 // P-384 269 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 270 CMSEccKeyStore.SZ_384_CRYPT_1)[0], 271 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 272 CMSEccKeyStore.SZ_384_CRYPT_2)[0], 273 // P-521 274 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 275 CMSEccKeyStore.SZ_521_CRYPT_1)[0], 276 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 277 CMSEccKeyStore.SZ_521_CRYPT_2)[0], 278 // x25519 279 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 280 CMSEccKeyStore.SZ_X25519)[0], 281 // x448 282 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, 283 CMSEccKeyStore.SZ_X448)[0] 284 285 }; 286 287 // the key encryption algorithms to be used 288 AlgorithmID[] keyEAs = { 289 AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme, 290 AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme, 291 AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme, 292 AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme, 293 AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme, 294 }; 295 // key wrap algorithms 296 AlgorithmID[] keyWrapAlgs = { 297 CMSAlgorithmID.cms_aes128_wrap, 298 CMSAlgorithmID.cms_aes192_wrap, 299 CMSAlgorithmID.cms_aes256_wrap, 300 }; 301 // whether to encrypt or authenticated encrypt 302 boolean[] doAuthEncrypt = { false, true }; 303 for (int h = 0; h < doAuthEncrypt.length; h++) { 304 boolean authEncrypt = doAuthEncrypt[h]; 305 for (int i = 0; i < keyEAs.length; i++) { 306 AlgorithmID[] contentEAs; 307 AlgorithmID keyEA = keyEAs[i]; 308 for (int j = 0; j < keyWrapAlgs.length; j++) { 309 AlgorithmID keyWrapAlg = keyWrapAlgs[j]; 310 int kekLength; 311 int keyLength; 312 if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes192_wrap)) { 313 kekLength = 192; 314 keyLength = 192; 315 contentEAs = (authEncrypt) ? 316 new AlgorithmID[] { AlgorithmID.aes192_GCM, AlgorithmID.aes192_CCM } : 317 new AlgorithmID[] { AlgorithmID.aes192_CBC }; 318 } else if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes128_wrap)) { 319 kekLength = 128; 320 keyLength = 128; 321 contentEAs = (authEncrypt) ? 322 new AlgorithmID[] { AlgorithmID.aes128_GCM, AlgorithmID.aes128_CCM } : 323 new AlgorithmID[] { AlgorithmID.aes128_CBC }; 324 } else { 325 kekLength = 256; 326 keyLength = 256; 327 contentEAs = (authEncrypt) ? 328 new AlgorithmID[] { AlgorithmID.aes256_GCM, AlgorithmID.aes256_CCM, AlgorithmID.chacha20Poly1305 } : 329 new AlgorithmID[] { AlgorithmID.aes256_CBC }; 330 } 331 332 for (int k = 0; k < contentEAs.length; k++) { 333 334 /**************************************************************************/ 335 /* */ 336 /* (Authenticated) Encryption Demo */ 337 /* */ 338 /**************************************************************************/ 339 340 AlgorithmID contentEA = contentEAs[k]; 341 // in practice we may not create one message for recipients with different strength; 342 // however, for simplicity we use all recipients here 343 System.out.println("Running " + (authEncrypt ? "authenticated" : "") + "encryption demo for " + 344 keyEA.getName() + " with " + keyWrapAlg.getName() +" and " + contentEA.getName()); 345 startEncryptionDemo(session, 346 contentEA, 347 keyLength, 348 keyEA, 349 keyWrapAlg, 350 kekLength, 351 authEncrypt, 352 recipientCerts); 353 354 355 /**************************************************************************/ 356 /* */ 357 /* Signing and (Authenticated) Encryption Demo */ 358 /* */ 359 /**************************************************************************/ 360 361 for (int l = 0; l < digestAndSignatureAlgorithms.length; l++) { 362 AlgorithmID digestAlg = digestAndSignatureAlgorithms[l][DIGEST_ALG]; 363 AlgorithmID signatureAlg = digestAndSignatureAlgorithms[l][SIGNATURE_ALG]; 364 PrivateKey signerKey = signerKeyAndCerts[l].getPrivateKey(); 365 X509Certificate[] signerCerts = signerKeyAndCerts[l].getCertificateChain(); 366 System.out.println("Running signing and " + (authEncrypt ? "authenticated" : "") + "encryption and demo for " + signatureAlg.getName()); 367 startSigningAndEncryptionDemo(session, 368 multipart, 369 digestAlg, 370 signatureAlg, 371 contentEA, 372 keyLength, 373 keyEA, 374 keyWrapAlg, 375 kekLength, 376 authEncrypt, 377 signerKey, 378 signerCerts, 379 recipientCerts); 380 } 381 } 382 } 383 } 384 } 385 } 386 387 /** 388 * Starts the signing demo. 389 * 390 * @param session the mail session 391 * @param multipart the multipart content of the message to be signed 392 * @param digestAlgorithm the digest algorithm to be used 393 * @param signatureAlgorithm the signature algorithm to be used 394 * @param signerKey the private key of the signer 395 * @param signerCerts the certificate chain of the signer 396 * 397 * @throws Exception if an error occurs 398 */ 399 public void startSigningDemo(Session session, 400 DataHandler multipart, 401 AlgorithmID digestAlgorithm, 402 AlgorithmID signatureAlgorithm, 403 PrivateKey signerKey, 404 X509Certificate[] signerCerts) throws Exception { 405 406 Message msg; // the message to send 407 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 408 ByteArrayInputStream bais; // we read from a stream 409 410 // This is an explicitly signed message 411 System.out.println("creating explicitly signed message..."); 412 msg = createSignedMessage(session, multipart, false, digestAlgorithm, signatureAlgorithm, signerKey, signerCerts); 413 baos.reset(); 414 msg.saveChanges(); 415 msg.writeTo(baos); 416 bais = new ByteArrayInputStream(baos.toByteArray()); 417 msg = new MimeMessage(session, bais); 418 if (PRINT_MESSAGES) { 419 printMessage(msg); 420 } 421 DumpMessage.dumpMsg(msg); 422 423 System.out.println("\n\n*****************************************\n\n"); 424 425 426 // This is an implicitly signed message 427 System.out.println("creating implicitly signed message..."); 428 msg = createSignedMessage(session, multipart, true, digestAlgorithm, signatureAlgorithm, signerKey, signerCerts); 429 baos.reset(); 430 msg.saveChanges(); 431 msg.writeTo(baos); 432 bais = new ByteArrayInputStream(baos.toByteArray()); 433 msg = new MimeMessage(session, bais); 434 if (PRINT_MESSAGES) { 435 printMessage(msg); 436 } 437 DumpMessage.dumpMsg(msg); 438 439 System.out.println("\n\n*****************************************\n\n"); 440 441 } 442 443 /** 444 * Starts the (maybe authenticated) encryption demo. 445 * 446 * @param session the mail session 447 * @param contentEA the content encryption algorithm to be used 448 * @param keyLength the length of the secret content encryption key to be created and used 449 * @param keyEA the (key agreement) algorithm to use for creating a shared secret 450 * key encryption key for encrypting the symmetric key 451 * (e.g. AlgorithmID.esdhKeyAgreement) 452 * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping) 453 * the content-encryption key with the shared key-encryption 454 * created according to the requested key agreement protocol 455 * @param kekLength the length of the shared key encryption key to be generated 456 * @param authEncrypt whether to create an authenticated encrypted message 457 * @param recipientCerts the certificates of the recipients 458 * 459 * @throws Exception if an error occurs 460 */ 461 public void startEncryptionDemo(Session session, 462 AlgorithmID contentEA, 463 int keyLength, 464 AlgorithmID keyEA, 465 AlgorithmID keyWrapAlgorithm, 466 int kekLength, 467 boolean authEncrypt, 468 X509Certificate[] recipientCerts) throws Exception { 469 470 471 Message msg; // the message to send 472 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 473 ByteArrayInputStream bais; // we read from a stream 474 475 System.out.println("creating encrypted message " + contentEA.getName()); 476 msg = createEncryptedMessage(session, (AlgorithmID)contentEA.clone(), keyLength, 477 (AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlgorithm.clone(), kekLength, authEncrypt, 478 recipientCerts); 479 480 baos.reset(); 481 msg.saveChanges(); 482 msg.writeTo(baos); 483 bais = new ByteArrayInputStream(baos.toByteArray()); 484 msg = new MimeMessage(session, bais); 485 if (PRINT_MESSAGES) { 486 printMessage(msg); 487 } 488 DumpMessage.dumpEncryptedMessage(msg); 489 490 System.out.println("\n\n*****************************************\n\n"); 491 492 493 } 494 495 496 /** 497 * Starts the signing + (maybe authenticated) encryption demo. 498 * 499 * @param session the mail session 500 * @param multipart the Multipart content to be signed and encrypted 501 * @param digestAlgorithm the digest algorithm to be used for signing 502 * @param signatureAlgorithm the signature algorithm to be used 503 * @param contentEA the content encryption algorithm to be used 504 * @param keyLength the length of the secret content encryption key to be created and used 505 * @param keyEA the (key agreement) algorithm to use for creating a shared secret 506 * key encryption key for encrypting the symmetric key 507 * (e.g. AlgorithmID.esdhKeyAgreement) 508 * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping) 509 * the content-encryption key with the shared key-encryption 510 * created according to the requested key agreement protocol 511 * @param kekLength the length of the shared key encryption key to be generated 512 * @param authEncrypt whether to create an authenticated encrypted message 513 * @param signerKey the private key of the signer 514 * @param signerCerts the certificate chain of the signer 515 * @param recipientCerts the certificates of the recipients 516 * 517 * @throws Exception if an error occurs 518 */ 519 public void startSigningAndEncryptionDemo(Session session, 520 DataHandler multipart, 521 AlgorithmID digestAlgorithm, 522 AlgorithmID signatureAlgorithm, 523 AlgorithmID contentEA, 524 int keyLength, 525 AlgorithmID keyEA, 526 AlgorithmID keyWrapAlgorithm, 527 int kekLength, 528 boolean authEncrypt, 529 PrivateKey signerKey, 530 X509Certificate[] signerCerts, 531 X509Certificate[] recipientCerts) throws Exception { 532 533 534 Message msg; // the message to send 535 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 536 ByteArrayInputStream bais; // we read from a stream 537 538 // Create an implicitly signed and authenticated encrypted message with attachment 539 System.out.println("creating implicitly signed and authenticated encrypted message " + contentEA.getName()); 540 msg = createSignedAndEncryptedMessage(session, digestAlgorithm, signatureAlgorithm, contentEA, keyLength, 541 keyEA, keyWrapAlgorithm, kekLength, multipart, true, authEncrypt, 542 signerKey, signerCerts, recipientCerts); 543 baos.reset(); 544 msg.saveChanges(); 545 msg.writeTo(baos); 546 bais = new ByteArrayInputStream(baos.toByteArray()); 547 msg = new MimeMessage(session, bais); 548 if (PRINT_MESSAGES) { 549 printMessage(msg); 550 } 551 DumpMessage.dumpEncryptedMessage(msg); 552 553 System.out.println("\n\n*****************************************\n\n"); 554 555 // Create an explicitly signed and authenticated encrypted message with attachment 556 System.out.println("creating explicitly signed and authenticated encrypted message " + contentEA.getName()); 557 msg = createSignedAndEncryptedMessage(session, digestAlgorithm, signatureAlgorithm, contentEA, keyLength, 558 keyEA, keyWrapAlgorithm, kekLength, multipart, true, authEncrypt, 559 signerKey, signerCerts, recipientCerts); 560 baos.reset(); 561 msg.saveChanges(); 562 msg.writeTo(baos); 563 bais = new ByteArrayInputStream(baos.toByteArray()); 564 msg = new MimeMessage(session, bais); 565 if (PRINT_MESSAGES) { 566 printMessage(msg); 567 } 568 DumpMessage.dumpEncryptedMessage(msg); 569 570 } 571 572 /** 573 * Creates a MIME message container with the given subject for the given session. 574 * 575 * @param session the mail sesion 576 * @param subject the subject of the message 577 * 578 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content) 579 * 580 * @throws MessagingException if the message cannot be created 581 */ 582 public Message createMessage(Session session, String subject) throws MessagingException { 583 MimeMessage msg = new MimeMessage(session); 584 msg.setFrom(new InternetAddress(from_)); 585 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to_, false)); 586 msg.setSentDate(new Date()); 587 msg.setSubject(subject); 588 return msg; 589 } 590 591 592 /** 593 * Creates a signed message. 594 * 595 * @param session the mail session 596 * @param dataHandler the content of the message to be signed 597 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 598 * (multipart/signed) signing 599 * @param digestAlgorithm the digest algorithm to be used 600 * @param signatureAlgorithm the signature algorithm to be used 601 * @param signerKey the private key of the signer 602 * @param signerCertificates the certificate chain of the signer 603 * 604 * @return the signed message 605 * 606 * @throws MessagingException if an error occurs when creating the message 607 */ 608 public Message createSignedMessage(Session session, 609 DataHandler dataHandler, 610 boolean implicit, 611 AlgorithmID digestAlgorithm, 612 AlgorithmID signatureAlgorithm, 613 PrivateKey signerKey, 614 X509Certificate[] signerCertificates) 615 throws MessagingException { 616 617 String subject = null; 618 StringBuffer buf = new StringBuffer(); 619 620 if (implicit) { 621 subject = "IAIK-S/MIME: Implicitly Signed"; 622 buf.append("This message is implicitly signed!\n"); 623 buf.append("You need an S/MIME aware mail client to view this message.\n"); 624 buf.append("\n\n"); 625 } else { 626 subject = "IAIK-S/MIME: Explicitly Signed"; 627 buf.append("This message is explicitly signed!\n"); 628 buf.append("Every mail client can view this message.\n"); 629 buf.append("Non S/MIME mail clients will show the signature as attachment.\n"); 630 buf.append("\n\n"); 631 } 632 633 Message msg = createMessage(session, subject); 634 635 SignedContent sc = new SignedContent(implicit); 636 if (dataHandler != null) { 637 sc.setDataHandler(dataHandler); 638 } else { 639 sc.setText(buf.toString()); 640 } 641 sc.setCertificates(signerCertificates); 642 643 try { 644 sc.addSigner(signerKey, 645 signerCertificates[0], 646 (AlgorithmID)digestAlgorithm.clone(), 647 (AlgorithmID)signatureAlgorithm.clone()); 648 } catch (NoSuchAlgorithmException ex) { 649 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 650 } 651 652 msg.setContent(sc, sc.getContentType()); 653 // let the SignedContent update some message headers 654 sc.setHeaders(msg); 655 return msg; 656 } 657 658 /** 659 * Creates an (maybe authenticated) encrypted message. 660 * 661 * @param session the mail session 662 * @param contentEA the content encryption algorithm to be used 663 * @param keyLength the length of the secret content encryption key to be created and used 664 * @param keyEA the (key agreement) algorithm to use for creating a shared secret 665 * key encryption key for encrypting the symmetric key 666 * (e.g. AlgorithmID.esdhKeyAgreement) 667 * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping) 668 * the content-encryption key with the shared key-encryption 669 * created according to the requested key agreement protocol 670 * @param kekLength the length of the shared key encryption key to be generated 671 * @param authEncrypt whether to create an authenticated encrypted message 672 * @param recipientCerts the certificates of the recipients 673 * 674 * @return the (maybe authenticated) encrypted message 675 * 676 * @throws MessagingException if an error occurs when creating the message 677 */ 678 public Message createEncryptedMessage(Session session, AlgorithmID contentEA, int keyLength, 679 AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength, boolean authEncrypt, 680 X509Certificate[] recipientCerts) 681 throws MessagingException { 682 683 AlgorithmID algorithm = (AlgorithmID)contentEA.clone(); 684 AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone(); 685 AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone(); 686 687 StringBuffer subject = new StringBuffer(); 688 subject.append("IAIK-S/MIME: " + (authEncrypt ? "Authenticated " : "") + " Encrypted ["+algorithm.getName()); 689 if (keyLength > 0) { 690 subject.append("/"+keyLength); 691 } 692 subject.append("]"); 693 Message msg = createMessage(session, subject.toString()); 694 695 EncryptedContent ec = null; 696 if (authEncrypt) { 697 ec = new AuthEncryptedContent(); 698 } else { 699 ec = new EncryptedContent(); 700 } 701 702 StringBuffer buf = new StringBuffer(); 703 buf.append("This is the " + (authEncrypt ? "authenticated " : "") + " encrypted content!\n"); 704 buf.append("Content encryption algorithm: "+algorithm.getName()); 705 buf.append("\n\n"); 706 707 ec.setText(buf.toString()); 708 709 // we use ephemeral-static ECDH 710 for (int i = 0; i < recipientCerts.length; i++) { 711 try { 712 ec.addRecipient(recipientCerts[i], keyAgreeAlg, keyWrapAlg, kekLength); 713 } catch (SMimeException ex) { 714 throw new MessagingException("Error adding ECDH recipient: " + ex.getMessage()); 715 } 716 } 717 718 try { 719 ec.setEncryptionAlgorithm(algorithm, keyLength); 720 } catch (NoSuchAlgorithmException ex) { 721 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 722 } 723 724 msg.setContent(ec, ec.getContentType()); 725 // let the EncryptedContent update some message headers 726 ec.setHeaders(msg); 727 728 return msg; 729 } 730 731 /** 732 * Creates a signed and (maybe authenticated) encrypted message. 733 * 734 * @param session the mail session 735 * @param digestAlgorithm the digest algorithm to be used for signing 736 * @param signatureAlgorithm the signature algorithm to be used 737 * @param contentEA the content encryption algorithm to be used 738 * @param keyLength the length of the secret content encryption key to be created and used 739 * @param keyEA the (key agreement) algorithm to use for creating a shared secret 740 * key encryption key for encrypting the symmetric key 741 * (e.g. AlgorithmID.esdhKeyAgreement) 742 * @param keyWrapAlgorithm the key wrap algorithm to be used for encrypting (wrapping) 743 * the content-encryption key with the shared key-encryption 744 * created according to the requested key agreement protocol 745 * @param kekLength the length of the shared key encryption key to be generated 746 * @param dataHandler the content of the message to be signed and encrypted 747 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 748 * (multipart/signed) signing 749 * @param authEncrypt whether to create an authenticated encrypted message 750 * @param signerKey the private key of the signer 751 * @param signerCerts the certificate chain of the signer 752 * @param recipientCerts the certificates of the recipients 753 * 754 * @return the signed and (maybe authenticated) encrypted message 755 * 756 * @throws MessagingException if an error occurs when creating the message 757 */ 758 public Message createSignedAndEncryptedMessage(Session session, 759 AlgorithmID digestAlgorithm, AlgorithmID signatureAlgorithm, 760 AlgorithmID contentEA, int keyLength, 761 AlgorithmID keyEA, AlgorithmID keyWrapAlgorithm, int kekLength, 762 DataHandler dataHandler, boolean implicit, boolean authEncrypt, 763 PrivateKey signerKey, X509Certificate[] signerCerts, 764 X509Certificate[] recipientCerts) 765 throws MessagingException { 766 767 String subject = null; 768 String text = null; 769 if (implicit) { 770 subject = "IAIK-S/MIME: Implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted"; 771 text = "This message is implicitly signed and " + (authEncrypt ? "authenticated " : "") + "encrypted!\n\n\n"; 772 } else { 773 subject = "IAIK-S/MIME: explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted"; 774 text = "This message is explicitly signed and " + (authEncrypt ? "authenticated " : "") + " encrypted!\n\n\n"; 775 } 776 Message msg = createMessage(session, subject); 777 778 SignedContent sc = new SignedContent(implicit); 779 if (dataHandler != null) { 780 sc.setDataHandler(dataHandler); 781 } else { 782 sc.setText(text); 783 } 784 sc.setCertificates(signerCerts); 785 try { 786 sc.addSigner(signerKey, 787 signerCerts[0], 788 (AlgorithmID)digestAlgorithm.clone(), 789 (AlgorithmID)signatureAlgorithm.clone()); 790 } catch (NoSuchAlgorithmException ex) { 791 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 792 } 793 794 EncryptedContent ec = null; 795 if (authEncrypt) { 796 ec = new AuthEncryptedContent(sc); 797 } else { 798 ec = new EncryptedContent(sc); 799 } 800 801 802 AlgorithmID algorithm = (AlgorithmID)contentEA.clone(); 803 AlgorithmID keyAgreeAlg = (AlgorithmID)keyEA.clone(); 804 AlgorithmID keyWrapAlg = (AlgorithmID)keyWrapAlgorithm.clone(); 805 806 // we use ephemeral-static DH 807 for (int i = 0; i < recipientCerts.length; i++) { 808 try { 809 ec.addRecipient(recipientCerts[i], keyAgreeAlg, keyWrapAlg, kekLength); 810 } catch (SMimeException ex) { 811 throw new MessagingException("Error adding ESDH recipient: " + ex.getMessage()); 812 } 813 } 814 815 // set the encryption algorithm 816 try { 817 ec.setEncryptionAlgorithm(algorithm, keyLength); 818 } catch (NoSuchAlgorithmException ex) { 819 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 820 } 821 msg.setContent(ec, ec.getContentType()); 822 // let the EncryptedContent update some message headers 823 ec.setHeaders(msg); 824 825 return msg; 826 } 827 828 829 830 /** 831 * Prints a dump of the given message to System.out. 832 * 833 * @param msg the message to be dumped to System.out 834 * 835 * @throws IOException if an I/O error occurs 836 */ 837 static void printMessage(Message msg) throws IOException { 838 System.out.println("------------------------------------------------------------------"); 839 System.out.println("Message dump: \n"); 840 try { 841 msg.writeTo(System.out); 842 } catch (MessagingException ex) { 843 throw new IOException(ex.getMessage()); 844 } 845 System.out.println("\n------------------------------------------------------------------"); 846 } 847 848 849 /** 850 * The main method. 851 */ 852 public static void main(String[] argv) throws Exception { 853 854 DemoSMimeUtil.initDemos(); 855 // add ECC provider 856 ECCDemoUtil.installIaikEccProvider(); 857 858 (new SMimeV4EccDemo()).start(); 859 System.out.println("\nReady!"); 860 DemoUtil.waitKey(); 861 } 862 }