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/ess/TripleWrappingDemo.java 38 12.02.25 17:59 Dbratko $ 059 // $Revision: 38 $ 060 // 061 062 package demo.smime.ess; 063 064 import iaik.asn1.structures.AlgorithmID; 065 import iaik.smime.EncryptedContent; 066 import iaik.smime.SMimeBodyPart; 067 import iaik.smime.SMimeMultipart; 068 import iaik.smime.SignedContent; 069 import iaik.x509.X509Certificate; 070 071 import java.io.ByteArrayInputStream; 072 import java.io.ByteArrayOutputStream; 073 import java.io.IOException; 074 import java.security.NoSuchAlgorithmException; 075 import java.security.PrivateKey; 076 import java.security.interfaces.RSAPrivateKey; 077 import java.util.Date; 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.Multipart; 084 import javax.mail.Session; 085 import javax.mail.internet.InternetAddress; 086 import javax.mail.internet.MimeBodyPart; 087 import javax.mail.internet.MimeMessage; 088 089 import demo.DemoSMimeUtil; 090 import demo.DemoUtil; 091 import demo.keystore.CMSKeyStore; 092 import demo.smime.DumpMessage; 093 094 /** 095 * An ESS triple wrapping demo. Creates a <a href="https://www.rfc-editor.org/rfc/rfc2634.html" target = "_blank">RFC2634</a> 096 * ESS triple wrapped (signed - encrypted - signed) message and subsequently parses it to 097 * decrypt the layer and verify the signatures. 098 * <p> 099 * To run this demo the following packages are required: 100 * <ul> 101 * <li> 102 * <code>iaik_cms.jar</code> 103 * </li> 104 * <li> 105 * <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>). 106 * </li> 107 * <li> 108 * <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>). 109 * </li> 110 * <li> 111 * <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). 112 * </li> 113 * </ul> 114 * 115 * @see iaik.smime.EncryptedContent 116 * @see iaik.smime.SignedContent 117 */ 118 public class TripleWrappingDemo { 119 120 final static boolean DEBUG = false; 121 122 // whether to print dump all generates test messages to System.out 123 final static boolean PRINT_MESSAGES = false; 124 125 String firstName = "John"; 126 String lastName = "SMime"; 127 String to = "smimetest@iaik.at"; // email recipient 128 String from = "smimetest@iaik.at"; // email sender 129 String host = "mailhost"; // name of the mailhost 130 131 X509Certificate[] signerCertificates1; // list of certificates to include in the S/MIME message 132 X509Certificate recipientCertificate; // certificate of the recipient 133 X509Certificate signerCertificate1; // certificate of the signer/sender 134 X509Certificate encryptionCertOfSigner1; // signer uses different certificate for encryption 135 PrivateKey signerPrivateKey1; // private key of the signer/sender 136 137 X509Certificate[] signerCertificates2; // if outer signer is different than inner signer 138 X509Certificate signerCertificate2; // certificate of the signer/sender 139 PrivateKey signerPrivateKey2; // private key of the signer/sender 140 141 /** 142 * Empty default constructor. Reads all required keys and certificates 143 * from the demo keystore (created by running @link demo.keystore.SetupCMSKeySrore) 144 * stored at "cms.keystore" in your current working directoy. 145 */ 146 public TripleWrappingDemo() { 147 148 System.out.println(); 149 System.out.println("******************************************************************************************"); 150 System.out.println("* TripleWrapping demo *"); 151 System.out.println("* (shows the usage of the IAIK-CMS library for creating/parsing a triple wrapped message *"); 152 System.out.println("******************************************************************************************"); 153 System.out.println(); 154 155 // get the certificates from the KeyStore 156 signerCertificates1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 157 signerPrivateKey1 = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 158 signerCertificate1 = signerCertificates1[0]; 159 160 // recipient = signer for this test 161 recipientCertificate = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 162 encryptionCertOfSigner1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 163 164 signerCertificates2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2); 165 signerPrivateKey2 = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_2); 166 signerCertificate2 = signerCertificates2[0]; 167 } 168 169 /** 170 * Starts the demo. 171 * 172 * @throws IOException if an I/O related error occurs 173 */ 174 public void start() throws IOException { 175 176 // get the default Session 177 Session session = DemoSMimeUtil.getSession(); 178 179 try { 180 // Create a demo Multipart 181 MimeBodyPart mbp1 = new SMimeBodyPart(); 182 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n"); 183 MimeBodyPart attachment = new SMimeBodyPart(); 184 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html"))); 185 attachment.setFileName("test.html"); 186 187 Multipart mp = new SMimeMultipart(); 188 mp.addBodyPart(mbp1); 189 mp.addBodyPart(attachment); 190 DataHandler multipart = new DataHandler(mp, mp.getContentType()); 191 192 Message msg; // the message to send 193 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 194 ByteArrayInputStream bais; // we read from a stream 195 196 // 1. implicitly signed - encrypted - implicitly signed; inner = outer signed 197 System.out.println("1. implicitly signed - encrypted - implicitly signed; inner = outer signed"); 198 msg = tripleWrap(session, multipart, true, true, false); 199 msg.writeTo(baos); 200 bais = new ByteArrayInputStream(baos.toByteArray()); 201 msg = new MimeMessage(session, bais); 202 if (PRINT_MESSAGES) { 203 printMessage(msg); 204 } 205 DumpMessage.dumpMsg(msg); 206 baos.reset(); 207 System.out.println("\n\n*****************************************\n\n"); 208 209 // 2. implicitly signed - encrypted - implicitly signed; inner != outer signed 210 System.out.println("2. implicitly signed - encrypted - implicitly signed; inner != outer signed"); 211 msg = tripleWrap(session, multipart, true, true, true); 212 msg.writeTo(baos); 213 bais = new ByteArrayInputStream(baos.toByteArray()); 214 msg = new MimeMessage(session, bais); 215 if (PRINT_MESSAGES) { 216 printMessage(msg); 217 } 218 DumpMessage.dumpMsg(msg); 219 baos.reset(); 220 System.out.println("\n\n*****************************************\n\n"); 221 222 // 3. implicitly signed - encrypted - explicitly signed; inner == outer signed 223 System.out.println("3. implicitly signed - encrypted - explicitly signed; inner = outer signed"); 224 msg = tripleWrap(session, multipart, true, false, false); 225 msg.writeTo(baos); 226 bais = new ByteArrayInputStream(baos.toByteArray()); 227 msg = new MimeMessage(session, bais); 228 if (PRINT_MESSAGES) { 229 printMessage(msg); 230 } 231 DumpMessage.dumpMsg(msg); 232 baos.reset(); 233 System.out.println("\n\n*****************************************\n\n"); 234 235 // 4. implicitly signed - encrypted - explicitly signed; inner != outer signed 236 System.out.println("4. implicitly signed - encrypted - explicitly signed; inner != outer signed"); 237 msg = tripleWrap(session, multipart, true, false, true); 238 msg.writeTo(baos); 239 bais = new ByteArrayInputStream(baos.toByteArray()); 240 msg = new MimeMessage(session, bais); 241 if (PRINT_MESSAGES) { 242 printMessage(msg); 243 } 244 DumpMessage.dumpMsg(msg); 245 baos.reset(); 246 System.out.println("\n\n*****************************************\n\n"); 247 248 // 5. explicitly signed - encrypted - implicitly signed; inner == outer signed 249 System.out.println("5. explicitly signed - encrypted - implicitly signed; inner = outer signed"); 250 msg = tripleWrap(session, multipart, false, true, false); 251 msg.writeTo(baos); 252 bais = new ByteArrayInputStream(baos.toByteArray()); 253 msg = new MimeMessage(session, bais); 254 if (PRINT_MESSAGES) { 255 printMessage(msg); 256 } 257 DumpMessage.dumpMsg(msg); 258 baos.reset(); 259 System.out.println("\n\n*****************************************\n\n"); 260 261 // 6. explicitly signed - encrypted - implicitly signed; inner != outer signed 262 System.out.println("6. explicitly signed - encrypted - implicitly signed; inner != outer signed"); 263 msg = tripleWrap(session, multipart, false, true, true); 264 msg.writeTo(baos); 265 bais = new ByteArrayInputStream(baos.toByteArray()); 266 msg = new MimeMessage(session, bais); 267 if (PRINT_MESSAGES) { 268 printMessage(msg); 269 } 270 DumpMessage.dumpMsg(msg); 271 baos.reset(); 272 System.out.println("\n\n*****************************************\n\n"); 273 274 // 7. explicitly signed - encrypted - explicitly signed; inner == outer signed 275 System.out.println("7. explicitly signed - encrypted - explicitly signed; inner = outer signed"); 276 msg = tripleWrap(session, multipart, false, false, false); 277 msg.writeTo(baos); 278 bais = new ByteArrayInputStream(baos.toByteArray()); 279 msg = new MimeMessage(session, bais); 280 if (PRINT_MESSAGES) { 281 printMessage(msg); 282 } 283 DumpMessage.dumpMsg(msg); 284 baos.reset(); 285 System.out.println("\n\n*****************************************\n\n"); 286 287 // 8. explicitly signed - encrypted - explicitly signed; inner != outer signed 288 System.out.println("8. explicitly signed - encrypted - explicitly signed; inner != outer signed"); 289 msg = tripleWrap(session, multipart, false, false, true); 290 msg.writeTo(baos); 291 bais = new ByteArrayInputStream(baos.toByteArray()); 292 msg = new MimeMessage(session, bais); 293 if (PRINT_MESSAGES) { 294 printMessage(msg); 295 } 296 DumpMessage.dumpMsg(msg); 297 baos.reset(); 298 System.out.println("\n\n*****************************************\n\n"); 299 300 System.out.println("Ready!"); 301 302 } catch (Exception ex) { 303 ex.printStackTrace(); 304 throw new RuntimeException(ex.toString()); 305 } 306 } 307 308 /** 309 * Creates a MIME message container with the given subject for the given session. 310 * 311 * @param session the mail sesion 312 * @param subject the subject of the message 313 * 314 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content) 315 * 316 * @throws MessagingException if the message cannot be created 317 */ 318 public Message createMessage(Session session, String subject) throws MessagingException { 319 MimeMessage msg = new MimeMessage(session); 320 msg.setFrom(new InternetAddress(from)); 321 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false)); 322 msg.setSentDate(new Date()); 323 msg.setSubject(subject); 324 return msg; 325 } 326 327 /** 328 * Creates a triple wrapped (signed - encrypted - signed) message. 329 * 330 * @param session the Session 331 * @param dataHandler the data handler providing the raw content 332 * @param innerImplicit whether to sign the inner content implicitly or explicitly 333 * @param outerImplicit whether to sign the outer content implicitly or explicitly 334 * @param differentOuterSigner whether to simulate a different outer signer receiving 335 * the signed and encrypted message and adding an outer 336 * layer 337 * 338 * @return the triple wrapped message 339 */ 340 public Message tripleWrap(Session session, DataHandler dataHandler, 341 boolean innerImplicit, boolean outerImplicit, 342 boolean differentOuterSigner) 343 throws Exception { 344 345 346 StringBuffer buf = new StringBuffer(); 347 String subject = "IAIK-S/MIME: TripleWrapped: "; 348 buf.append("This is a triple wrapped message where "); 349 if (innerImplicit) { 350 subject += "implicit signed - encrypted - "; 351 buf.append("the inner content is implicit signed\n"); 352 } else { 353 subject += "explicit signed - encrypted - "; 354 buf.append("the inner content is explicit signed\n"); 355 } 356 if (outerImplicit) { 357 subject += "implicit signed"; 358 buf.append("and the outer content is implicit signed\n"); 359 } else { 360 subject += "explicit signed"; 361 buf.append("and the outer content is explicit signed\n"); 362 } 363 364 Message msg = createMessage(session, subject); 365 366 367 // create the inner signed content 368 SignedContent sc1 = new SignedContent(innerImplicit); 369 if (dataHandler != null) { 370 sc1.setDataHandler(dataHandler); 371 } else { 372 sc1.setText(buf.toString()); 373 } 374 sc1.setCertificates(signerCertificates1); 375 try { 376 sc1.addSigner((RSAPrivateKey)signerPrivateKey1, signerCertificate1); 377 } catch (NoSuchAlgorithmException ex) { 378 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 379 } 380 381 // create the enrypted ("middle") layer 382 EncryptedContent ec = new EncryptedContent(sc1); 383 ec.setSMimeType(); 384 // encrypt for the recipient 385 ec.addRecipient(recipientCertificate, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 386 // I want to be able to decrypt the message, too 387 ec.addRecipient(encryptionCertOfSigner1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 388 // set the encryption algorithm 389 ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256); 390 391 if (differentOuterSigner) { 392 // just do a receiving sending step inbetween 393 Message msg1 = createMessage(session, "IAIK-S/MIME: Signed and encrypted"); 394 msg1.setContent(ec, ec.getContentType()); 395 ec.setHeaders(msg1); 396 msg1.saveChanges(); 397 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 398 msg1.writeTo(baos); 399 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 400 msg1 = new MimeMessage(session, bais); 401 if (PRINT_MESSAGES) { 402 printMessage(msg1); 403 } 404 // optionally parse message here 405 DumpMessage.dumpMsg(msg1); 406 ec = (EncryptedContent)msg1.getContent(); 407 } 408 409 // create the signed outer layer 410 SignedContent sc2 = new SignedContent(ec, outerImplicit); 411 412 if (differentOuterSigner) { 413 sc2.setCertificates(signerCertificates2); 414 try { 415 sc2.addSigner((RSAPrivateKey)signerPrivateKey2, signerCertificate2); 416 } catch (NoSuchAlgorithmException ex) { 417 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 418 } 419 } else { 420 sc2.setCertificates(signerCertificates1); 421 try { 422 sc2.addSigner((RSAPrivateKey)signerPrivateKey1, signerCertificate1); 423 } catch (NoSuchAlgorithmException ex) { 424 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 425 } 426 } 427 msg.setContent(sc2, sc2.getContentType()); 428 // let the EncryptedContent update some message headers 429 sc2.setHeaders(msg); 430 msg.saveChanges(); 431 return msg; 432 } 433 434 /** 435 * Prints a dump of the given message to System.out. 436 * 437 * @param msg the message to be dumped to System.out 438 */ 439 private static void printMessage(Message msg) throws IOException { 440 System.out.println("------------------------------------------------------------------"); 441 System.out.println("Message dump: \n"); 442 try { 443 msg.writeTo(System.out); 444 } catch (MessagingException ex) { 445 throw new IOException(ex.getMessage()); 446 } 447 System.out.println("\n------------------------------------------------------------------"); 448 } 449 450 /** 451 * Main method. 452 */ 453 public static void main(String[] argv) throws IOException { 454 455 DemoSMimeUtil.initDemos(); 456 (new TripleWrappingDemo()).start(); 457 System.out.println("\nReady!"); 458 DemoUtil.waitKey(); 459 } 460 }