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/pkcs11/EncryptedMailDemo.java 19 12.02.25 17:59 Dbratko $
059 // $Revision: 19 $
060 //
061
062 package demo.smime.pkcs11;
063
064 // class and interface imports
065 import iaik.asn1.structures.AlgorithmID;
066 import iaik.cms.CMSException;
067 import iaik.smime.EncryptedContent;
068 import iaik.smime.SMimeBodyPart;
069 import iaik.smime.SMimeMultipart;
070 import iaik.smime.SMimeUtil;
071 import iaik.utils.Util;
072 import iaik.x509.X509Certificate;
073
074 import java.io.ByteArrayInputStream;
075 import java.io.ByteArrayOutputStream;
076 import java.io.IOException;
077 import java.security.GeneralSecurityException;
078 import java.security.Key;
079 import java.security.NoSuchAlgorithmException;
080 import java.security.PrivateKey;
081 import java.security.cert.Certificate;
082 import java.security.interfaces.RSAPrivateKey;
083 import java.util.Date;
084 import java.util.Enumeration;
085
086 import javax.activation.DataHandler;
087 import javax.activation.FileDataSource;
088 import javax.mail.Message;
089 import javax.mail.MessagingException;
090 import javax.mail.Multipart;
091 import javax.mail.Session;
092 import javax.mail.internet.InternetAddress;
093 import javax.mail.internet.MimeBodyPart;
094 import javax.mail.internet.MimeMessage;
095
096 import demo.DemoSMimeUtil;
097 import demo.DemoUtil;
098 import demo.cms.pkcs11.PKCS11Demo;
099 import demo.smime.DumpMessage;
100
101
102
103 /**
104 * This class shows how to en- and decrypt an S/MIME message
105 * using the PKCS#11 provider for accessing the private key
106 * on a smart card. This implementation uses the <code>SecurityProvider</code>
107 * feature of the CMS implementation of the IAIK-CMS toolkit.
108 * <p>
109 * To run this demo the following packages are required:
110 * <ul>
111 * <li>
112 * <code>iaik_cms.jar</code>
113 * </li>
114 * <li>
115 * <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>).
116 * </li>
117 * <li>
118 * <code>iaikPkcs11Provider.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/pkcs11-provider/" target="_blank">IAIK PKCS#11 Provider</a>).
119 * </li>
120 * <li>
121 * <code>iaikPkcs11Wrapper.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/pkcs11-wrapper/" target="_blank">IAIK PKCS#11 Wrapper</a>).
122 * </li>
123 * <li>
124 * The shared PKCS#11 library (<code>pkcs11wrapper.dll</code> for Windows, <code>libpkcs11wrapper.so</code> for Unix); contained in the IAIK PKCS#11 Wrapper library.
125 * </li>
126 * <li>
127 * <code>iaik_eccelerate.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">IAIK ECC Library</a>, if you want to use Elliptic Curve Cryptography).
128 * </li>
129 * <li>
130 * <code>mail.jar</code> (<a href="http://www.oracle.com/technetwork/java/javamail/index.html" target="_blank">JavaMail API</a>).
131 * </li>
132 * <li>
133 * <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).
134 * </li>
135 * </ul>
136 * <code>iaik_cms.jar</code>, <code>iaik_cms_demo.jar</code>, <code>iaik_jce(full).jar</code>,
137 * <code>iaikPkcs11Wrapper.jar</code> and <code>iaikPkcs11Provider.jar</code> (and
138 * <code>iaik_eccelerate.jar</code>, <code>mail.jar</code>, <code>activation.jar</code>) have to be put
139 * into the classpath, the shared library (<code>pkcs11wrapper.dll</code> or <code>libpkcs11wrapper.so</code>)
140 * has to be in your system library search path or in your VM library path, e.g. (on Windows,
141 * assuming that all jar files are located in a lib sub-directory and the dll is in a lib/win64
142 * sub-directory):
143 * <pre>
144 * java -Djava.library.path=lib/win64
145 * -cp lib/iaik_jce.jar;lib/iaikPkcs11Wrapper.jar;lib/iaikPkcs11Provider.jar;lib/iaik_cms.jar;lib/iaik_cms_demo.jar;lib/mail.jar;lib/activation.jar
146 * demo.pkcs11.EncryptedMailDemo <pkcs11Module>.dll
147 * </pre>
148 */
149 public class EncryptedMailDemo extends PKCS11Demo {
150
151 //whether to print dump all generates test messages to System.out
152 final static boolean PRINT_MESSAGES = true;
153
154 /**
155 * Default email address. Used in this demo if the recipient certificate
156 * does not contain an email address.
157 */
158 private final static String DEFAULT_EMAIL = "smimetest@iaik.tugraz.at";
159
160
161 /**
162 * The private key of the recipient. In this case only a proxy object, but the
163 * application cannot see this. Used for decryption.
164 */
165 protected PrivateKey privateKey_;
166
167 /**
168 * The certificate of the recipient. In contrast to the private key, the
169 * certificate holds holds the actual (public) keying material.
170 * Used for encryption.
171 */
172 protected X509Certificate certificate_;
173
174 /**
175 * The email address of the sender.
176 */
177 protected String sender_;
178
179 /**
180 * The email address of the recipient.
181 */
182 protected String recipient_;
183
184 /**
185 * Creates a EncryptedMailDemo object for the given module name.
186 *
187 * @param moduleName the name of the module
188 * @param userPin the user-pin (password) for the TokenKeyStore
189 * (may be <code>null</code> to pou-up a dialog asking for the pin)
190 */
191 public EncryptedMailDemo(String moduleName, char[] userPin) {
192 // install provider in super class
193 super(moduleName, userPin);
194 System.out.println();
195 System.out.println("************************************************************************************************************");
196 System.out.println("* PKCS#11 EncryptedMailDemo *");
197 System.out.println("* (shows how to en/decrypt S/MIME messages using the IAIK-PKCS11 provider for accessing the key on a card) *");
198 System.out.println("************************************************************************************************************");
199 System.out.println();
200 }
201
202
203 /**
204 * This method gets the key store of the PKCS#11 provider and searches for a
205 * certificate and corresponding private key entry that can en/decrypt the data.
206 * Key and cert are stored in the <code>privateKey_</code> and <code>certificate_</code>
207 * member variables. Usually you only will have the smartcard on the decryption
208 * side (i.e. the sender will get the certificate by other means to use it
209 * for encrypting the message), however, for simplicity (and since we do not know
210 * which certificate/card you are actually will use for running the demo) we
211 * get both, key and certificate from the card.
212 *
213 * @throws GeneralSecurityException If anything with the provider fails.
214 * @throws IOException If loading the key store fails.
215 */
216 public void getKeyAndCertificate()
217 throws GeneralSecurityException, IOException, CMSException
218 {
219
220 // we simply take the first keystore, if there are serveral
221 Enumeration aliases = tokenKeyStore_.aliases();
222
223 PrivateKey privateKey = null;
224 X509Certificate certificate = null;
225 // and we take the first private key for simplicity
226 while (aliases.hasMoreElements()) {
227 String keyAlias = aliases.nextElement().toString();
228 Key key = null;
229 try {
230 key = tokenKeyStore_.getKey(keyAlias, null);
231 } catch (NoSuchAlgorithmException ex) {
232 throw new GeneralSecurityException(ex.toString());
233 }
234 if (key instanceof RSAPrivateKey) {
235 Certificate[] certificateChain = tokenKeyStore_.getCertificateChain(keyAlias);
236 if ((certificateChain != null) && (certificateChain.length > 0)) {
237 X509Certificate[] certificates = Util.convertCertificateChain(certificateChain);
238 X509Certificate userCertificate = certificates[0];
239 boolean[] keyUsage = userCertificate.getKeyUsage();
240 if ((keyUsage == null) || keyUsage[2] || keyUsage[3]) { // check for encryption, but also accept if none set
241 // check if there is a receipient info for this certificate
242 certificate = userCertificate;
243 privateKey = (PrivateKey)key;
244 // email address included in recipient certificate?
245 String[] emailAddresses = SMimeUtil.getEmailAddresses(certificates[0]);
246 if (emailAddresses.length > 0) {
247 // in this demo we use same email for sender and recipient
248 sender_ = emailAddresses[0];
249 recipient_ = emailAddresses[0];
250 privateKey_ = privateKey;
251 certificate_ = certificate;
252 break;
253 }
254
255 }
256 }
257 }
258 }
259
260 if (privateKey_ == null) {
261 if (privateKey == null) {
262 System.out.println("Found no decryption key. Ensure that the correct card is inserted and contains a key that is suitable for decryption.");
263 System.exit(0);
264 }
265 // we did not find a certificate containing an email address
266 privateKey_ = privateKey;
267 certificate_ = certificate;
268 // use default address
269 sender_ = DEFAULT_EMAIL;
270 recipient_ = DEFAULT_EMAIL;
271 }
272 System.out.println("##########");
273 System.out.println("The decrpytion key is: " + privateKey_);
274 System.out.println("##########");
275 System.out.println("##########");
276 System.out.println("The encryption certificate is:");
277 System.out.println(certificate_.toString());
278 System.out.println("##########");
279 }
280
281
282 /**
283 * Creates an encrypted message.
284 *
285 * @param session the mail session
286 * @param dataHandler the content of the message to be encrypted
287 *
288 * @return the encrypted message
289 *
290 * @throws MessagingException if an error occurs when creating the message
291 */
292 protected MimeMessage createEncryptedMessage(Session session, DataHandler dataHandler)
293 throws MessagingException {
294
295 String subject = "IAIK-S/MIME PKCS11 Demo: Encrypted Mail";
296 String text = "This message is encrypted with AES!\n";
297
298
299 // create EncryptedContent object
300 EncryptedContent ec = new EncryptedContent();
301
302 if (dataHandler != null) {
303 ec.setDataHandler(dataHandler);
304 } else {
305 ec.setText(text);
306 }
307
308 ec.addRecipient(certificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
309 try {
310 ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
311 } catch (NoSuchAlgorithmException ex) {
312 throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());
313 }
314
315
316 // create MimeMessage
317 MimeMessage msg = new MimeMessage(session);
318 msg.setFrom(new InternetAddress(sender_));
319 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient_, false));
320 msg.setSentDate(new Date());
321 msg.setSubject(subject);
322 // set encrypted content
323 msg.setContent(ec, ec.getContentType());
324 // let the EncryptedContent update some message headers
325 ec.setHeaders(msg);
326 return msg;
327 }
328
329
330 /**
331 * Starts the demo.
332 */
333 public void start() {
334 try {
335 Session session = DemoSMimeUtil.getSession();
336 getKeyStore();
337 getKeyAndCertificate();
338 // Create a demo contentMultipart
339 MimeBodyPart mbp1 = new SMimeBodyPart();
340 mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
341 // attachment
342 MimeBodyPart attachment = new SMimeBodyPart();
343 attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
344 attachment.setFileName("test.html");
345
346 Multipart mp = new SMimeMultipart();
347 mp.addBodyPart(mbp1);
348 mp.addBodyPart(attachment);
349 DataHandler multipart = new DataHandler(mp, mp.getContentType());
350
351 // create signed message
352 MimeMessage msg = createEncryptedMessage(DemoSMimeUtil.getSession(), multipart);
353
354 // we write to a stream
355 ByteArrayOutputStream baos = new ByteArrayOutputStream();
356 msg.saveChanges();
357 msg.writeTo(baos); // here you could call Transport.send if you want to send the message
358
359 // we read from a stream
360 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
361 // parse message
362 msg = new MimeMessage(session, bais);
363 if (PRINT_MESSAGES) {
364 printMessage(msg);
365 }
366 DumpMessage dumpMsg = new DumpMessage();
367 dumpMsg.setRecipientKey(privateKey_);
368 dumpMsg.dump(msg);
369 System.out.println("##########");
370 } catch (Throwable ex) {
371 ex.printStackTrace();
372 throw new RuntimeException(ex.toString());
373 }
374 }
375
376 /**
377 * This is the main method that is called by the JVM during startup.
378 *
379 * @param args These are the command line arguments.
380 */
381 public static void main(String[] args) {
382
383 if (args.length == 0) {
384 System.out.println("Missing pkcs11 module name.\n");
385 printUsage();
386 }
387
388 String moduleName = args[0];
389 char[] userPin = (args.length == 2) ? args[1].toCharArray() : null;
390
391 if (args.length > 2) {
392 System.out.println("Too many arguments.\n");
393 printUsage();
394 }
395
396 DemoSMimeUtil.initDemos();
397
398 (new EncryptedMailDemo(moduleName, userPin)).start();;
399 System.out.println("Ready!");
400 DemoUtil.waitKey();
401 }
402
403 /**
404 * Prints a dump of the given message to System.out.
405 *
406 * @param msg the message to be dumped to System.out
407 */
408 private static void printMessage(Message msg) throws IOException {
409 System.out.println("------------------------------------------------------------------");
410 System.out.println("Message dump: \n");
411 try {
412 msg.writeTo(System.out);
413 } catch (MessagingException ex) {
414 throw new IOException(ex.getMessage());
415 }
416 System.out.println("\n------------------------------------------------------------------");
417 }
418
419 /**
420 * Print usage information.
421 */
422 private final static void printUsage() {
423 System.out.println("Usage:\n");
424 System.out.println("java EncryptedMailDemo <pkcs11 module name> [<user-pin>]\n");
425 System.out.println("e.g.:");
426 System.out.println("java EncryptedMailDemo aetpkss1.dll");
427 System.out.println("java EncryptedMailDemo aetpkss1.so");
428 DemoUtil.waitKey();
429 System.exit(0);
430 }
431
432
433
434
435 }