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/basic/SMimeOaepPssDemo.java 8 12.02.25 17:58 Dbratko $ 059 // $Revision: 8 $ 060 // 061 062 package demo.smime.basic; 063 064 import iaik.asn1.structures.AlgorithmID; 065 import iaik.cms.CMSException; 066 import iaik.cms.Utils; 067 import iaik.smime.EncryptedContent; 068 import iaik.smime.SMimeBodyPart; 069 import iaik.smime.SMimeMultipart; 070 import iaik.smime.SignedContent; 071 import iaik.x509.X509Certificate; 072 073 import java.io.ByteArrayInputStream; 074 import java.io.ByteArrayOutputStream; 075 import java.io.IOException; 076 import java.security.InvalidAlgorithmParameterException; 077 import java.security.NoSuchAlgorithmException; 078 import java.security.PrivateKey; 079 import java.util.Date; 080 081 import javax.activation.DataHandler; 082 import javax.activation.FileDataSource; 083 import javax.mail.Message; 084 import javax.mail.MessagingException; 085 import javax.mail.Multipart; 086 import javax.mail.Session; 087 import javax.mail.internet.InternetAddress; 088 import javax.mail.internet.MimeBodyPart; 089 import javax.mail.internet.MimeMessage; 090 091 import demo.DemoSMimeUtil; 092 import demo.DemoUtil; 093 import demo.cms.envelopedData.OaepEnvelopedDataDemo; 094 import demo.cms.signedData.PssSignedDataDemo; 095 import demo.keystore.CMSKeyStore; 096 import demo.smime.DumpMessage; 097 098 /** 099 * This class demonstrates the usage of the IAIK S/MIME implementation using RSA-PSS for 100 * signing, RSA-OAEP for key transport and AES-256 CBC for content encryption. It shows how to create 101 * signed and/or encrypted S/MIME messages and how to parse them and verify the signature 102 * and decrypt the content, respectively. 103 * <p> 104 * To run this demo the following packages are required: 105 * <ul> 106 * <li> 107 * <code>iaik_cms.jar</code> (IAIK-CMS/SMIME) 108 * </li> 109 * <li> 110 * <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>). 111 * </li> 112 * <li> 113 * <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>). 114 * </li> 115 * <li> 116 * <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). 117 * </li> 118 * </ul> 119 */ 120 public class SMimeOaepPssDemo { 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"; // name of sender 126 String lastName_ = "SMime"; 127 String from_ = "smimetest@iaik.tugraz.at"; // email sender 128 String to_ = "smimetest@iaik.tugraz.at"; // email recipient 129 String host_ = "mailhost"; // name of the mailhost 130 131 X509Certificate[] signerCertificates_; // list of certificates to include in the S/MIME message 132 X509Certificate recipientCertificate_; // certificate of the recipient 133 X509Certificate signerCertificate_; // certificate of the signer/sender 134 PrivateKey signerPrivateKey_; // private key of the signer/sender 135 136 AlgorithmID hashID_; // the hash algorithm to be used 137 int saltLength_; // the salt length to be used 138 139 /** 140 * Default constructor. Reads certificates and keys from the demo keystore. 141 */ 142 public SMimeOaepPssDemo() { 143 144 System.out.println(); 145 System.out.println("******************************************************************************************"); 146 System.out.println("* SMimeDemo demo *"); 147 System.out.println("* (shows how to create and parse (verify, decrypt) signed and encrypted S/MIME messages) *"); 148 System.out.println("******************************************************************************************"); 149 System.out.println(); 150 151 // get the certificates from the KeyStore 152 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 153 signerPrivateKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 154 signerCertificate_ = signerCertificates_[0]; 155 156 // recipient = signer for this test 157 recipientCertificate_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 158 159 hashID_ = AlgorithmID.sha256; 160 saltLength_ = 32; 161 } 162 163 /** 164 * Starts the demo. 165 * 166 * @throws IOException if an I/O related error occurs 167 */ 168 public void start() throws IOException { 169 170 // get the default Session 171 Session session = DemoSMimeUtil.getSession(); 172 173 try { 174 // Create a demo Multipart 175 MimeBodyPart mbp1 = new SMimeBodyPart(); 176 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n"); 177 // attachment 178 MimeBodyPart attachment = new SMimeBodyPart(); 179 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html"))); 180 attachment.setFileName("test.html"); 181 182 Multipart mp = new SMimeMultipart(); 183 mp.addBodyPart(mbp1); 184 mp.addBodyPart(attachment); 185 DataHandler multipart = new DataHandler(mp, mp.getContentType()); 186 187 Message msg; // the message to send 188 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream 189 ByteArrayInputStream bais; // we read from a stream 190 191 // 1. This is a plain message 192 msg = createPlainMessage(session, multipart); 193 System.out.println("creating plain message..."); 194 msg.saveChanges(); 195 msg.writeTo(baos); 196 bais = new ByteArrayInputStream(baos.toByteArray()); 197 msg = new MimeMessage(session, bais); 198 if (PRINT_MESSAGES) { 199 printMessage(msg); 200 } 201 DumpMessage.dumpMsg(msg); 202 203 System.out.println("\n\n*****************************************\n\n"); 204 205 // 2. This is an explicitly signed message 206 msg = createSignedMessage(session, multipart, false); 207 System.out.println("creating explicitly signed message..."); 208 baos.reset(); 209 msg.saveChanges(); 210 msg.writeTo(baos); 211 bais = new ByteArrayInputStream(baos.toByteArray()); 212 msg = new MimeMessage(session, bais); 213 if (PRINT_MESSAGES) { 214 printMessage(msg); 215 } 216 DumpMessage.dumpMsg(msg); 217 218 System.out.println("\n\n*****************************************\n\n"); 219 220 221 // 3. This is an implicitly signed message 222 msg = createSignedMessage(session, multipart, true); 223 System.out.println("creating implicitly signed message..."); 224 baos.reset(); 225 msg.saveChanges(); 226 msg.writeTo(baos); 227 bais = new ByteArrayInputStream(baos.toByteArray()); 228 msg = new MimeMessage(session, bais); 229 if (PRINT_MESSAGES) { 230 printMessage(msg); 231 } 232 DumpMessage.dumpMsg(msg); 233 234 System.out.println("\n\n*****************************************\n\n"); 235 236 // 4. Now create encrypted message 237 238 msg = createEncryptedMessage(session, (AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256); 239 System.out.println("creating encrypted message [AES/256]..."); 240 baos.reset(); 241 msg.saveChanges(); 242 msg.writeTo(baos); 243 bais = new ByteArrayInputStream(baos.toByteArray()); 244 msg = new MimeMessage(session, bais); 245 if (PRINT_MESSAGES) { 246 printMessage(msg); 247 } 248 DumpMessage.dumpMsg(msg); 249 250 System.out.println("\n\n*****************************************\n\n"); 251 252 253 254 // 5. Now create a implicitly signed and encrypted message with attachment 255 System.out.println("creating implicitly signed and encrypted message [AES/256]..."); 256 msg = createSignedAndEncryptedMessage(session, multipart, true); 257 baos.reset(); 258 msg.saveChanges(); 259 msg.writeTo(baos); 260 bais = new ByteArrayInputStream(baos.toByteArray()); 261 msg = new MimeMessage(session, bais); 262 if (PRINT_MESSAGES) { 263 printMessage(msg); 264 } 265 DumpMessage.dumpMsg(msg); 266 267 System.out.println("\n\n*****************************************\n\n"); 268 269 // 6. Now create a explicitly signed and encrypted message with attachment 270 System.out.println("creating explicitly signed and encrypted message [AES/256]..."); 271 msg = createSignedAndEncryptedMessage(session, multipart, false); 272 baos.reset(); 273 msg.saveChanges(); 274 msg.writeTo(baos); 275 bais = new ByteArrayInputStream(baos.toByteArray()); 276 msg = new MimeMessage(session, bais); 277 if (PRINT_MESSAGES) { 278 printMessage(msg); 279 } 280 DumpMessage.dumpMsg(msg); 281 282 System.out.println("\n\n*****************************************\n\n"); 283 284 285 286 } catch (Exception ex) { 287 ex.printStackTrace(); 288 throw new RuntimeException(ex.toString()); 289 } 290 } 291 292 /** 293 * Creates a MIME message container with the given subject for the given session. 294 * 295 * @param session the mail sesion 296 * @param subject the subject of the message 297 * 298 * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content) 299 * 300 * @throws MessagingException if the message cannot be created 301 */ 302 public Message createMessage(Session session, String subject) throws MessagingException { 303 MimeMessage msg = new MimeMessage(session); 304 msg.setFrom(new InternetAddress(from_)); 305 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to_, false)); 306 msg.setSentDate(new Date()); 307 msg.setSubject(subject); 308 return msg; 309 } 310 311 /** 312 * Creates a simple plain (neither signed nor encrypted) message. 313 * 314 * @param session the mail session 315 * @param dataHandler the content of the message 316 * 317 * @return the plain message 318 * 319 * @throws MessagingException if an error occurs when creating the message 320 */ 321 public Message createPlainMessage(Session session, DataHandler dataHandler) throws MessagingException { 322 323 Message msg = createMessage(session, "IAIK-S/MIME: Plain message"); 324 if (dataHandler != null) { 325 msg.setDataHandler(dataHandler); 326 } else { 327 msg.setText("This is a plain message!\nIt is wether signed nor encrypted!\n"); 328 } 329 return msg; 330 } 331 332 /** 333 * Creates a signed and encrypted message. 334 * 335 * @param session the mail session 336 * @param dataHandler the content of the message to be signed and encrypted 337 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 338 * (multipart/signed) signing 339 * 340 * @return the signed and encrypted message 341 * 342 * @throws MessagingException if an error occurs when creating the message 343 */ 344 public Message createSignedAndEncryptedMessage(Session session, DataHandler dataHandler, boolean implicit) 345 throws MessagingException { 346 347 String subject = null; 348 String text = null; 349 if (implicit) { 350 subject = "IAIK-S/MIME: Implicitly Signed and Encrypted"; 351 text = "This message is implicitly signed and encrypted!\n\n\n"; 352 } else { 353 subject = "IAIK-S/MIME: Explicitly Signed and Encrypted"; 354 text = "This message is explicitly signed and encrypted!\n\n\n"; 355 } 356 Message msg = createMessage(session, subject); 357 358 SignedContent sc = new SignedContent(implicit); 359 if (dataHandler != null) { 360 sc.setDataHandler(dataHandler); 361 } else { 362 sc.setText(text); 363 } 364 sc.setCertificates(signerCertificates_); 365 try { 366 AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone(); 367 AlgorithmID rsaPssID = PssSignedDataDemo.createPssAlgorithmID(hashID_, mgfID, saltLength_); 368 sc.addSigner(signerPrivateKey_, signerCertificate_, hashID_, rsaPssID); 369 } catch (NoSuchAlgorithmException ex) { 370 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 371 } catch (InvalidAlgorithmParameterException ex) { 372 throw new MessagingException("Cannot create PSS parameters: " + ex.getMessage(), ex); 373 } 374 375 376 EncryptedContent ec = new EncryptedContent(sc); 377 // encrypt for the recipient 378 try { 379 // empty label 380 byte[] label = {}; 381 AlgorithmID rsaOaepID = Utils.createOaepAlgorithmID(hashID_); 382 ec.addRecipient(recipientCertificate_, rsaOaepID); 383 ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256); 384 } catch (NoSuchAlgorithmException ex) { 385 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 386 } catch (Exception ex) { 387 throw new MessagingException("Cannot create OAEP parameters: " + ex.getMessage()); 388 } 389 msg.setContent(ec, ec.getContentType()); 390 // let the EncryptedContent update some message headers 391 ec.setHeaders(msg); 392 393 return msg; 394 } 395 396 /** 397 * Creates a signed message. 398 * 399 * @param session the mail session 400 * @param dataHandler the content of the message to be signed 401 * @param implicit whether to use implicit (application/pkcs7-mime) or explicit 402 * (multipart/signed) signing 403 * 404 * @return the signed message 405 * 406 * @throws MessagingException if an error occurs when creating the message 407 */ 408 public Message createSignedMessage(Session session, 409 DataHandler dataHandler, boolean implicit) 410 throws MessagingException { 411 412 String subject = null; 413 StringBuffer buf = new StringBuffer(); 414 415 if (implicit) { 416 subject = "IAIK-S/MIME: Implicitly Signed"; 417 buf.append("This message is implicitly signed!\n"); 418 buf.append("You need an S/MIME aware mail client to view this message.\n"); 419 buf.append("\n\n"); 420 } else { 421 subject = "IAIK-S/MIME: Explicitly Signed"; 422 buf.append("This message is explicitly signed!\n"); 423 buf.append("Every mail client can view this message.\n"); 424 buf.append("Non S/MIME mail clients will show the signature as attachment.\n"); 425 buf.append("\n\n"); 426 } 427 428 Message msg = createMessage(session, subject); 429 430 SignedContent sc = new SignedContent(implicit); 431 432 if (dataHandler != null) { 433 sc.setDataHandler(dataHandler); 434 } else { 435 sc.setText(buf.toString()); 436 } 437 sc.setCertificates(signerCertificates_); 438 439 try { 440 AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone(); 441 AlgorithmID rsaPssID = PssSignedDataDemo.createPssAlgorithmID(hashID_, mgfID, saltLength_); 442 sc.addSigner(signerPrivateKey_, signerCertificate_, hashID_, rsaPssID); 443 } catch (NoSuchAlgorithmException ex) { 444 throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex); 445 } catch (InvalidAlgorithmParameterException ex) { 446 throw new MessagingException("Cannot create PSS parameters: " + ex.getMessage(), ex); 447 } 448 449 msg.setContent(sc, sc.getContentType()); 450 // let the SignedContent update some message headers 451 sc.setHeaders(msg); 452 return msg; 453 } 454 455 /** 456 * Creates an encrypted message. 457 * 458 * @param session the mail session 459 * @param algorithm the content encryption algorithm to be used 460 * @param keyLength the length of the secret content encryption key to be created and used 461 * 462 * @return the encrypted message 463 * 464 * @throws MessagingException if an error occurs when creating the message 465 */ 466 public Message createEncryptedMessage(Session session, AlgorithmID algorithm, int keyLength) 467 throws MessagingException { 468 469 StringBuffer subject = new StringBuffer(); 470 subject.append("IAIK-S/MIME: Encrypted ["+algorithm.getName()); 471 if (keyLength > 0) { 472 subject.append("/"+keyLength); 473 } 474 subject.append("]"); 475 Message msg = createMessage(session, subject.toString()); 476 477 EncryptedContent ec = new EncryptedContent(); 478 479 StringBuffer buf = new StringBuffer(); 480 buf.append("This is the encrypted content!\n"); 481 buf.append("Content encryption algorithm: "+algorithm.getName()); 482 buf.append("\n\n"); 483 484 ec.setText(buf.toString()); 485 486 // encrypt for the recipient 487 try { 488 AlgorithmID mgfID = (AlgorithmID)AlgorithmID.mgf1.clone(); 489 AlgorithmID pSourceID = (AlgorithmID)AlgorithmID.pSpecified.clone(); 490 // empty label 491 byte[] label = {}; 492 AlgorithmID rsaOaepID = Utils.createOaepAlgorithmID(hashID_); 493 ec.addRecipient(recipientCertificate_, rsaOaepID); 494 ec.setEncryptionAlgorithm(algorithm, keyLength); 495 } catch (NoSuchAlgorithmException ex) { 496 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage()); 497 } catch (Exception ex) { 498 throw new MessagingException("Cannot create OAEP parameters: " + ex.getMessage()); 499 } 500 501 msg.setContent(ec, ec.getContentType()); 502 // let the EncryptedContent update some message headers 503 ec.setHeaders(msg); 504 505 return msg; 506 } 507 508 509 510 511 512 /** 513 * Prints a dump of the given message to System.out. 514 * 515 * @param msg the message to be dumped to System.out 516 */ 517 static void printMessage(Message msg) throws IOException { 518 System.out.println("------------------------------------------------------------------"); 519 System.out.println("Message dump: \n"); 520 try { 521 msg.writeTo(System.out); 522 } catch (MessagingException ex) { 523 throw new IOException(ex.getMessage()); 524 } 525 System.out.println("\n------------------------------------------------------------------"); 526 } 527 528 529 /** 530 * The main method. 531 */ 532 public static void main(String[] argv) throws IOException { 533 534 DemoSMimeUtil.initDemos(); 535 (new SMimeOaepPssDemo()).start(); 536 System.out.println("\nReady!"); 537 DemoUtil.waitKey(); 538 } 539 }