001// Copyright (C) 2002 IAIK
002// https://sic.tech/
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// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/smime/pkcs11/EncryptedMailDemo.java 19    12.02.25 17:59 Dbratko $
029// $Revision: 19 $
030//
031
032package demo.smime.pkcs11;
033
034// class and interface imports
035import iaik.asn1.structures.AlgorithmID;
036import iaik.cms.CMSException;
037import iaik.smime.EncryptedContent;
038import iaik.smime.SMimeBodyPart;
039import iaik.smime.SMimeMultipart;
040import iaik.smime.SMimeUtil;
041import iaik.utils.Util;
042import iaik.x509.X509Certificate;
043
044import java.io.ByteArrayInputStream;
045import java.io.ByteArrayOutputStream;
046import java.io.IOException;
047import java.security.GeneralSecurityException;
048import java.security.Key;
049import java.security.NoSuchAlgorithmException;
050import java.security.PrivateKey;
051import java.security.cert.Certificate;
052import java.security.interfaces.RSAPrivateKey;
053import java.util.Date;
054import java.util.Enumeration;
055
056import jakarta.activation.DataHandler;
057import jakarta.activation.FileDataSource;
058import jakarta.mail.Message;
059import jakarta.mail.MessagingException;
060import jakarta.mail.Multipart;
061import jakarta.mail.Session;
062import jakarta.mail.internet.InternetAddress;
063import jakarta.mail.internet.MimeBodyPart;
064import jakarta.mail.internet.MimeMessage;
065
066import demo.DemoSMimeUtil;
067import demo.DemoUtil;
068import demo.cms.pkcs11.PKCS11Demo;
069import demo.smime.DumpMessage;
070
071
072
073/**
074 * This class shows how to en- and decrypt an S/MIME message 
075 * using the PKCS#11 provider for accessing the private key
076 * on a smart card. This implementation uses the <code>SecurityProvider</code> 
077 * feature of the CMS implementation of the IAIK-CMS toolkit.
078 * <p>
079 * To run this demo the following packages are required:
080 * <ul>
081 *    <li>
082 *       <code>iaik_cms.jar</code>
083 *    </li>
084 *    <li>
085 *       <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>).
086 *    </li>
087 *    <li>
088 *       <code>iaikPkcs11Provider.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/pkcs11-provider/" target="_blank">IAIK PKCS#11 Provider</a>).
089 *    </li>
090 *    <li>
091 *       <code>iaikPkcs11Wrapper.jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/pkcs11-wrapper/" target="_blank">IAIK PKCS#11 Wrapper</a>).
092 *    </li>
093 *    <li>
094 *       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.
095 *    </li>
096 *    <li>
097 *       <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).
098 *    </li>   
099 *    <li>
100 *       <a href="https://jakarta.ee/specifications/mail/" target="_blank">Jakarta</a>/<a href="https://eclipse-ee4j.github.io/angus-mail/" target="_blank">Angus</a> Mail
101 *    </li>   
102 *    <li>
103 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
104 *    </li> 
105 * </ul>
106 * <code>iaik_cms.jar</code>, <code>iaik_cms_demo.jar</code>, <code>iaik_jce(full).jar</code>,
107 * <code>iaikPkcs11Wrapper.jar</code> and <code>iaikPkcs11Provider.jar</code> (and
108 * <code>iaik_eccelerate.jar</code>, <code>mail.jar</code>, <code>activation.jar</code>) have to be put
109 * into the classpath, the shared library (<code>pkcs11wrapper.dll</code> or <code>libpkcs11wrapper.so</code>) 
110 * has to be in your system library search path or in your VM library path, e.g. (on Windows, 
111 * assuming that all jar files are located in a lib sub-directory and the dll is in a lib/win64
112 * sub-directory):
113 * <pre>
114 * java -Djava.library.path=lib/win64 
115 *      -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
116 *      demo.pkcs11.EncryptedMailDemo  &lt;pkcs11Module&gt;.dll
117 * </pre>
118 */
119public class EncryptedMailDemo extends PKCS11Demo {
120  
121  //whether to print dump all generates test messages to System.out
122  final static boolean PRINT_MESSAGES = true;   
123  
124  /**
125   * Default email address. Used in this demo if the recipient certificate
126   * does not contain an email address.
127   */
128  private final static String DEFAULT_EMAIL = "smimetest@iaik.tugraz.at";
129  
130
131  /**
132   * The private key of the recipient. In this case only a proxy object, but the
133   * application cannot see this. Used for decryption.
134   */
135  protected PrivateKey privateKey_;
136
137  /**
138   * The certificate of the recipient. In contrast to the private key, the
139   * certificate holds holds the actual (public) keying material.
140   * Used for encryption.
141   */
142  protected X509Certificate certificate_;
143  
144  /**
145   * The email address of the sender.
146   */
147  protected String sender_;
148  
149  /**
150   * The email address of the recipient.
151   */
152  protected String recipient_;
153
154  /**
155   * Creates a EncryptedMailDemo object for the given module name.
156   * 
157   * @param moduleName the name of the module
158   * @param userPin the user-pin (password) for the TokenKeyStore
159   *                (may be <code>null</code> to pou-up a dialog asking for the pin)
160   */
161  public EncryptedMailDemo(String moduleName, char[] userPin) {
162    // install provider in super class
163    super(moduleName, userPin);
164    System.out.println();
165    System.out.println("************************************************************************************************************");
166    System.out.println("*                                         PKCS#11 EncryptedMailDemo                                        *");
167    System.out.println("* (shows how to en/decrypt S/MIME messages using the IAIK-PKCS11 provider for accessing the key on a card) *");
168    System.out.println("************************************************************************************************************");
169    System.out.println();
170  }
171  
172
173  /**
174   * This method gets the key store of the PKCS#11 provider and searches for a
175   * certificate and corresponding private key entry that can en/decrypt the data.
176   * Key and cert are stored in the <code>privateKey_</code> and <code>certificate_</code>
177   * member variables. Usually you only will have the smartcard on the decryption
178   * side (i.e. the sender will get the certificate by other means to use it
179   * for encrypting the message), however, for simplicity (and since we do not know
180   * which certificate/card you are actually will use for running the demo) we
181   * get both, key and certificate from the card.
182   *
183   * @throws GeneralSecurityException If anything with the provider fails.
184   * @throws IOException If loading the key store fails.
185   */
186  public void getKeyAndCertificate()
187      throws GeneralSecurityException, IOException, CMSException
188  {
189    
190    // we simply take the first keystore, if there are serveral
191    Enumeration aliases = tokenKeyStore_.aliases();
192    
193    PrivateKey privateKey = null;
194    X509Certificate certificate = null;
195    // and we take the first private key for simplicity
196    while (aliases.hasMoreElements()) {
197      String keyAlias = aliases.nextElement().toString();
198      Key key = null;
199      try {
200        key = tokenKeyStore_.getKey(keyAlias, null);
201      } catch (NoSuchAlgorithmException ex) {
202        throw new GeneralSecurityException(ex.toString());
203      }
204      if (key instanceof RSAPrivateKey) {
205        Certificate[] certificateChain = tokenKeyStore_.getCertificateChain(keyAlias);
206        if ((certificateChain != null) && (certificateChain.length > 0)) {
207          X509Certificate[] certificates = Util.convertCertificateChain(certificateChain);
208          X509Certificate userCertificate = certificates[0];
209          boolean[] keyUsage = userCertificate.getKeyUsage();
210          if ((keyUsage == null) || keyUsage[2] || keyUsage[3]) { // check for encryption, but also accept if none set
211            // check if there is a receipient info for this certificate
212            certificate = userCertificate;
213            privateKey = (PrivateKey)key;
214            // email address included in recipient certificate?
215            String[] emailAddresses = SMimeUtil.getEmailAddresses(certificates[0]);
216            if (emailAddresses.length > 0) {
217              // in this demo we use same email for sender and recipient
218              sender_ = emailAddresses[0];
219              recipient_ = emailAddresses[0];
220              privateKey_ = privateKey;
221              certificate_ = certificate;
222              break;
223            }
224                
225          }
226        }  
227      }
228    }
229
230    if (privateKey_ == null) {
231      if (privateKey == null) {
232        System.out.println("Found no decryption key. Ensure that the correct card is inserted and contains a key that is suitable for decryption.");
233        System.exit(0);
234      }
235      // we did not find a certificate containing an email address
236      privateKey_ = privateKey;
237      certificate_ = certificate;
238      // use default address
239      sender_ = DEFAULT_EMAIL;
240      recipient_ = DEFAULT_EMAIL;
241    }
242    System.out.println("##########");
243    System.out.println("The decrpytion key is: " + privateKey_);
244    System.out.println("##########");
245    System.out.println("##########");
246    System.out.println("The encryption certificate is:");
247    System.out.println(certificate_.toString());
248    System.out.println("##########");
249  }
250  
251  
252  /**
253   * Creates an encrypted message.
254   *
255   * @param session the mail session
256   * @param dataHandler the content of the message to be encrypted
257   * 
258   * @return the encrypted message
259   *
260   * @throws MessagingException if an error occurs when creating the message
261   */
262  protected MimeMessage createEncryptedMessage(Session session, DataHandler dataHandler)
263    throws MessagingException {
264
265    String subject = "IAIK-S/MIME PKCS11 Demo: Encrypted Mail";
266    String text = "This message is encrypted with AES!\n";
267    
268   
269    // create EncryptedContent object
270    EncryptedContent ec = new EncryptedContent();
271
272    if (dataHandler != null) {
273      ec.setDataHandler(dataHandler);
274    } else {
275      ec.setText(text);
276    }
277 
278    ec.addRecipient(certificate_, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
279    try {
280      ec.setEncryptionAlgorithm((AlgorithmID)AlgorithmID.aes256_CBC.clone(), 256);
281    } catch (NoSuchAlgorithmException ex) {
282      throw new MessagingException("Content encryption algorithm not supported: " + ex.getMessage());   
283    }   
284
285    
286    // create MimeMessage
287    MimeMessage msg = new MimeMessage(session);
288    msg.setFrom(new InternetAddress(sender_));
289    msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient_, false));
290    msg.setSentDate(new Date());
291    msg.setSubject(subject);
292    // set encrypted content
293    msg.setContent(ec, ec.getContentType());
294    // let the EncryptedContent update some message headers
295    ec.setHeaders(msg);
296    return msg;
297  }
298  
299 
300  /**
301   * Starts the demo.
302   */
303  public void start() {
304    try {
305      Session session = DemoSMimeUtil.getSession();
306      getKeyStore();
307      getKeyAndCertificate();
308      // Create a demo contentMultipart
309      MimeBodyPart mbp1 = new SMimeBodyPart();
310      mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
311        // attachment
312      MimeBodyPart attachment = new SMimeBodyPart();
313      attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
314      attachment.setFileName("test.html");
315        
316      Multipart mp = new SMimeMultipart();
317      mp.addBodyPart(mbp1);
318      mp.addBodyPart(attachment);
319      DataHandler multipart = new DataHandler(mp, mp.getContentType());
320      
321      // create signed message
322      MimeMessage msg = createEncryptedMessage(DemoSMimeUtil.getSession(), multipart);
323      
324      //  we write to a stream
325      ByteArrayOutputStream baos = new ByteArrayOutputStream();
326      msg.saveChanges();
327      msg.writeTo(baos); // here you could call Transport.send if you want to send the message
328      
329      // we read from a stream
330      ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
331      // parse message
332      msg = new MimeMessage(session, bais);
333      if (PRINT_MESSAGES) {
334        printMessage(msg);
335      }
336      DumpMessage dumpMsg = new DumpMessage();
337      dumpMsg.setRecipientKey(privateKey_);
338      dumpMsg.dump(msg);
339      System.out.println("##########");
340    } catch (Throwable ex) {
341      ex.printStackTrace();
342      throw new RuntimeException(ex.toString());
343    }
344  }
345  
346  /**
347   * This is the main method that is called by the JVM during startup.
348   *
349   * @param args These are the command line arguments.
350   */
351  public static void main(String[] args) {
352
353    if (args.length == 0) {
354      System.out.println("Missing pkcs11 module name.\n");
355      printUsage();
356    }
357    
358    String moduleName = args[0];
359    char[] userPin = (args.length == 2) ? args[1].toCharArray() : null;
360    
361    if (args.length > 2) {
362      System.out.println("Too many arguments.\n");
363      printUsage();
364    }
365    
366    DemoSMimeUtil.initDemos();
367    
368    (new EncryptedMailDemo(moduleName, userPin)).start();;
369    System.out.println("Ready!");
370    DemoUtil.waitKey();
371  }
372  
373  /** 
374   * Prints a dump of the given message to System.out.
375   *
376   * @param msg the message to be dumped to System.out
377   */
378  private static void printMessage(Message msg) throws IOException {
379    System.out.println("------------------------------------------------------------------");
380    System.out.println("Message dump: \n");
381    try {
382      msg.writeTo(System.out);
383    } catch (MessagingException ex) {
384      throw new IOException(ex.getMessage());   
385    }    
386    System.out.println("\n------------------------------------------------------------------");
387  }  
388  
389  /**
390   * Print usage information.
391   */
392  private final static void printUsage() {
393    System.out.println("Usage:\n");
394    System.out.println("java EncryptedMailDemo <pkcs11 module name> [<user-pin>]\n");
395    System.out.println("e.g.:");
396    System.out.println("java EncryptedMailDemo aetpkss1.dll");
397    System.out.println("java EncryptedMailDemo aetpkss1.so");
398    DemoUtil.waitKey();
399    System.exit(0);
400  }
401
402
403
404
405}