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