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