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