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/cms/pkcs11/EnvelopedDataStreamDemo.java 18 12.02.25 17:58 Dbratko $ 059 // $Revision: 18 $ 060 // 061 062 package demo.cms.pkcs11; 063 064 // class and interface imports 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.cms.CMSException; 067 import iaik.cms.CertificateIdentifier; 068 import iaik.cms.EnvelopedDataStream; 069 import iaik.cms.KeyIdentifier; 070 import iaik.cms.KeyTransRecipientInfo; 071 import iaik.cms.RecipientInfo; 072 import iaik.x509.X509Certificate; 073 074 import java.io.ByteArrayInputStream; 075 import java.io.ByteArrayOutputStream; 076 import java.io.IOException; 077 import java.io.InputStream; 078 import java.security.GeneralSecurityException; 079 import java.security.InvalidKeyException; 080 import java.security.Key; 081 import java.security.NoSuchAlgorithmException; 082 import java.security.PrivateKey; 083 import java.security.cert.Certificate; 084 import java.security.interfaces.RSAPrivateKey; 085 import java.util.Enumeration; 086 087 import demo.DemoUtil; 088 089 090 091 /** 092 * This class shows how to en- and decrypt data with the CMS EnvelopedData 093 * type using the PKCS#11 provider for accessing the private key 094 * on a smart card. This implementation uses the <code>SecurityProvider</code> 095 * feature of the CMS implementation of the IAIK-CMS toolkit. 096 * <p> 097 * For running this demo the following packages are required (in addition to 098 * <code>iaik_cms.jar</code> and <code>iaik_cms_demo.jar</code>): 099 * <ul> 100 * <li> 101 * <code>iaik_jce(full).jar</code> (IAIK-JCE crypto toolkit) 102 * </li> 103 * <li> 104 * <code>iaikPkcs11Wrapper.jar</code> (IAIK PKCS#11 Wrapper) 105 * </li> 106 * <li> 107 * <code>iaikPkcs11Provider.jar</code> (IAIK PKCS#11 Provider) 108 * </li> 109 * <li> 110 * The shared PKCS#11 library (<code>pkcs11wrapper.dll</code> for Windows 111 * and <code>libpkcs11wrapper.so</code> for Unix) 112 * </li> 113 * </ul> 114 * <code>iaik_cms.jar</code>, <code>iaik_cms_demo.jar</code>, <code>iaik_jce(full).jar</code>, 115 * <code>iaikPkcs11Wrapper.jar</code> and <code>iaikPkcs11Provider.jar</code> have to 116 * be put into the classpath, the shared library (<code>pkcs11wrapper.dll</code> or 117 * <code>libpkcs11wrapper.so</code>) has to be in your system library search path 118 * or in your VM library path, e.g. (on Windows, assuming that all jar files are 119 * located in a lib sub-directory and the dll is in a lib/win64 sub-directory): 120 * <pre> 121 * java -Djava.library.path=lib/win64 122 * -cp lib/iaik_jce.jar;lib/iaikPkcs11Wrapper.jar;lib/iaikPkcs11Provider.jar;lib/iaik_cms.jar;lib/iaik_cms_demo.jar 123 * demo.pkcs11.ImplicitSignedDataStreamDemo <pkcs11Module>.dll 124 * </pre> 125 */ 126 public class EnvelopedDataStreamDemo extends PKCS11Demo { 127 128 /** 129 * The private key of the recipient. In this case only a proxy object, but the 130 * application cannot see this. Used for decryption. 131 */ 132 protected PrivateKey privateKey_; 133 134 /** 135 * The certificate of the recipient. In contrast to the private key, the 136 * certificate holds holds the actual (public) keying material. 137 * Used for encryption. 138 */ 139 protected X509Certificate certificate_; 140 141 /** 142 * Creates a EnvelopedDataStreamDemo object for the given module name. 143 * 144 * @param moduleName the name of the module 145 * @param userPin the user-pin (password) for the TokenKeyStore 146 * (may be <code>null</code> to pou-up a dialog asking for the pin) 147 * 148 */ 149 public EnvelopedDataStreamDemo(String moduleName, char[] userPin) { 150 // install provider in super class 151 super(moduleName, userPin); 152 System.out.println(); 153 System.out.println("************************************************************************************************"); 154 System.out.println("* PKCS#11 EnvelopedDataStreamDemo *"); 155 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation with the IAIK-PKCS11 provider) *"); 156 System.out.println("************************************************************************************************"); 157 System.out.println(); 158 } 159 160 161 /** 162 * This method gets the key store of the PKCS#11 provider and searches for a 163 * certificate and corresponding private key entry that can en/decrypt the data. 164 * Key and cert are stored in the <code>privateKey_</code> and <code>certificate_</code> 165 * member variables. Usually you only will have the smartcard on the decryption 166 * side (i.e. the sender will get the certificate by other means to use it 167 * for encrypting the message), however, for simplicity (and since we do not know 168 * which certificate/card you are actually will use for running the demo) we 169 * get both, key and certificate from the card. 170 * 171 * @throws GeneralSecurityException If anything with the provider fails. 172 * @throws IOException If loading the key store fails. 173 */ 174 public void getKeyAndCertificate() 175 throws GeneralSecurityException, IOException, CMSException 176 { 177 178 // we simply take the first keystore, if there are serveral 179 Enumeration aliases = tokenKeyStore_.aliases(); 180 181 // and we take the first private key for simplicity 182 while (aliases.hasMoreElements()) { 183 String keyAlias = aliases.nextElement().toString(); 184 Key key = null; 185 try { 186 key = tokenKeyStore_.getKey(keyAlias, null); 187 } catch (NoSuchAlgorithmException ex) { 188 throw new GeneralSecurityException(ex.toString()); 189 } 190 if (key instanceof RSAPrivateKey) { 191 Certificate[] certificateChain = tokenKeyStore_.getCertificateChain(keyAlias); 192 if ((certificateChain != null) && (certificateChain.length > 0)) { 193 java.security.cert.X509Certificate userCertificate = (java.security.cert.X509Certificate)certificateChain[0]; 194 boolean[] keyUsage = userCertificate.getKeyUsage(); 195 if ((keyUsage == null) || keyUsage[2] || keyUsage[3]) { // check for encryption, but also accept if none set 196 // check if there is a receipient info for this certificate 197 certificate_ = (userCertificate instanceof iaik.x509.X509Certificate) 198 ? (iaik.x509.X509Certificate) userCertificate 199 : new iaik.x509.X509Certificate(userCertificate.getEncoded()); 200 System.out.println("##########"); 201 privateKey_ = (PrivateKey) key; 202 System.out.println("The decrpytion key is: " + privateKey_); 203 System.out.println("##########"); 204 System.out.println("##########"); 205 System.out.println("The encryption certificate is:"); 206 System.out.println(certificate_.toString()); 207 System.out.println("##########"); 208 break; 209 } 210 } 211 } 212 } 213 214 if (privateKey_ == null) { 215 System.out.println("Found no decryption key. Ensure that the correct card is inserted and contains a key that is suitable for decryption."); 216 System.exit(0); 217 } 218 } 219 220 /** 221 * This method uses the CMS EnvelopedData type to encrypt the given data. It uses the 222 * certificate in the member variable set by <code>getKeyAndCertificate()</code>. 223 * 224 * @throws GeneralSecurityException 225 * If anything with the provider fails. 226 * @throws IOException 227 * If an I/O error occurs. 228 * @throws CMSException If handling the CMS data fails. 229 */ 230 public byte[] encrypt(byte[] data) 231 throws GeneralSecurityException, IOException, CMSException 232 { 233 System.out.println("##########"); 234 System.out.print("Encrypting data... "); 235 236 ByteArrayInputStream dataInputStream = new ByteArrayInputStream(data); 237 EnvelopedDataStream envelopedData = null; 238 try { 239 envelopedData = new EnvelopedDataStream(dataInputStream, 240 (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 241 } catch (NoSuchAlgorithmException ex) { 242 throw new GeneralSecurityException(ex.toString()); 243 } 244 245 // create RecipientInfo 246 X509Certificate recipientCertificate = certificate_; 247 RecipientInfo recipient = 248 new KeyTransRecipientInfo(recipientCertificate, 249 CertificateIdentifier.ISSUER_AND_SERIALNUMBER, 250 (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 251 252 envelopedData.setRecipientInfos(new RecipientInfo[] { recipient } ); 253 254 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 255 envelopedData.writeTo(baos); 256 257 return baos.toByteArray(); 258 } 259 260 261 /** 262 * This method decrypts the data from the provided CMS EnvelopedData. 263 * It uses the key and certificate in the member variables set by 264 * <code>getKeyAndCertificate()</code>. 265 * 266 * @throws GeneralSecurityException 267 * If anything with the provider fails. 268 * @throws IOException 269 * If an I/O error occurs. 270 * @throws CMSException If handling the CMS data fails. 271 */ 272 public byte[] decrypt(byte[] encodedEnvelopedData) 273 throws GeneralSecurityException, IOException, CMSException 274 { 275 System.out.println("##########"); 276 System.out.print("Decrypting data... "); 277 278 InputStream inputStream = new ByteArrayInputStream(encodedEnvelopedData); 279 EnvelopedDataStream envelopedData = new EnvelopedDataStream(inputStream); 280 281 RecipientInfo[] recipientInfos = envelopedData.getRecipientInfos(); 282 System.out.println("Included RecipientInfos: "); 283 for (int recipientIndex = 0; recipientIndex < recipientInfos.length; recipientIndex++) { 284 System.out.print("Recipient Info " + (recipientIndex+1) + ": "); 285 KeyIdentifier[] keyIdentifiers = recipientInfos[recipientIndex].getRecipientIdentifiers(); 286 for (int keyIdentifierIndex = 0; keyIdentifierIndex < keyIdentifiers.length; keyIdentifierIndex++) { 287 System.out.print(keyIdentifiers[keyIdentifierIndex]); 288 } 289 System.out.println(); 290 } 291 292 // setup cipher engine for decryption 293 try { 294 envelopedData.setupCipher(privateKey_, certificate_); 295 } catch (InvalidKeyException ex) { 296 throw new GeneralSecurityException(ex.toString()); 297 } catch (NoSuchAlgorithmException ex) { 298 throw new GeneralSecurityException(ex.toString()); 299 } 300 301 // read and decrypt data 302 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 303 InputStream dataInput = envelopedData.getInputStream(); 304 byte[] buffer = new byte[2048]; 305 int bytesRead; 306 while ((bytesRead = dataInput.read(buffer)) >= 0) { 307 // write to output 308 baos.write(buffer, 0, bytesRead); 309 } 310 311 System.out.println("##########"); 312 return baos.toByteArray(); 313 } 314 315 /** 316 * Starts the demo. 317 */ 318 public void start() { 319 try { 320 byte[] testMessage = "This is the test message to be encrypted!".getBytes("ASCII"); 321 getKeyStore(); 322 getKeyAndCertificate(); 323 // encrypt 324 byte[] envelopedData = encrypt(testMessage); 325 // decrypt 326 byte[] content = decrypt(envelopedData); 327 System.out.println("##########"); 328 // we know that we had a text content, thus we can convert into a String 329 System.out.println("Content: " + new String(content, "ASCII")); 330 System.out.println("##########"); 331 } catch (Throwable ex) { 332 ex.printStackTrace(); 333 throw new RuntimeException(ex.toString()); 334 } 335 } 336 337 /** 338 * This is the main method that is called by the JVM during startup. 339 * 340 * @param args These are the command line arguments. 341 */ 342 public static void main(String[] args) { 343 344 if (args.length == 0) { 345 System.out.println("Missing pkcs11 module name.\n"); 346 printUsage(); 347 } 348 349 String moduleName = args[0]; 350 char[] userPin = (args.length == 2) ? args[1].toCharArray() : null; 351 352 if (args.length > 2) { 353 System.out.println("Too many arguments.\n"); 354 printUsage(); 355 } 356 357 DemoUtil.initDemos(); 358 359 (new EnvelopedDataStreamDemo(moduleName, userPin)).start(); 360 System.out.println("\nReady!"); 361 DemoUtil.waitKey(); 362 } 363 364 /** 365 * Print usage information. 366 */ 367 private final static void printUsage() { 368 System.out.println("Usage:\n"); 369 System.out.println("java EnvelopedDataStreamDemo <pkcs11 module name> [<user-pin>]\n"); 370 System.out.println("e.g.:"); 371 System.out.println("java EnvelopedDataStreamDemo aetpkss1.dll"); 372 System.out.println("java EnvelopedDataStreamDemo aetpkss1.so"); 373 DemoUtil.waitKey(); 374 System.exit(0); 375 } 376 377 378 379 380 }