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