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 }