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 <pkcs11Module>.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}