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 }