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