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 &lt; 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  &lt;pkcs11Module&gt;.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    }