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 }