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/envelopedData/CAST128EnvelopedDataDemo.java 21 12.02.25 17:58 Dbratko $
059 // $Revision: 21 $
060 //
061
062 package demo.cms.envelopedData;
063
064 import iaik.asn1.INTEGER;
065 import iaik.asn1.OCTET_STRING;
066 import iaik.asn1.ObjectID;
067 import iaik.asn1.SEQUENCE;
068 import iaik.asn1.structures.AlgorithmID;
069 import iaik.cms.CMSException;
070 import iaik.cms.EncryptedContentInfoStream;
071 import iaik.cms.EnvelopedDataStream;
072 import iaik.cms.KeyTransRecipientInfo;
073 import iaik.cms.RecipientInfo;
074 import iaik.cms.SecurityProvider;
075 import iaik.security.random.SecRandom;
076 import iaik.utils.Util;
077 import iaik.x509.X509Certificate;
078
079 import java.io.ByteArrayInputStream;
080 import java.io.ByteArrayOutputStream;
081 import java.io.IOException;
082 import java.io.InputStream;
083 import java.security.NoSuchAlgorithmException;
084 import java.security.PrivateKey;
085 import java.security.SecureRandom;
086 import java.security.spec.AlgorithmParameterSpec;
087
088 import javax.crypto.KeyGenerator;
089 import javax.crypto.SecretKey;
090 import javax.crypto.spec.IvParameterSpec;
091
092 import demo.DemoUtil;
093 import demo.keystore.CMSKeyStore;
094
095 /**
096 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages
097 * for the CAST128 algorithm.
098 * <p>
099 * This demo compares the usage of class EnvelopedDataStream for encrypting the content
100 * using CAST128 with automatical (transparent) key/parameter handling against explicit
101 * key/parameter/EncrypedContentInfoStream handling.
102 * <p>
103 * All keys and certificates are read from a keystore created by the
104 * SetupCMSKeyStore program.
105 * <p>
106 * CAST parameters are defined as:
107 * <pre>
108 * Parameters ::= SEQUENCE {
109 * iv OCTET STRING DEFAULT 0,
110 * keyLength INTEGER }
111 * </pre>
112 */
113 public class CAST128EnvelopedDataDemo {
114
115 // certificate of user 1
116 X509Certificate user1;
117 // private key of user 1
118 PrivateKey user1_pk;
119 // certificate of user 2
120 X509Certificate user2;
121 // private key of user 2
122 PrivateKey user2_pk;
123 // secure random number generator
124 SecureRandom random;
125
126 /**
127 * Setup the demo certificate chains.
128 *
129 * Keys and certificate are retrieved from the demo KeyStore.
130 *
131 * @throws IOException if an file read error occurs
132 */
133 public CAST128EnvelopedDataDemo() throws IOException {
134
135 System.out.println();
136 System.out.println("**********************************************************************************");
137 System.out.println("* CAST128EnvelopedDataDemo *");
138 System.out.println("* (shows the usage of the EnvelopedData type for the CAST128 cipher) *");
139 System.out.println("**********************************************************************************");
140 System.out.println();
141 // add all certificates to the list
142 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
143 user1 = certs[0];
144 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
145 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
146 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
147
148 random = SecRandom.getDefault();
149 }
150
151 /**
152 * Creates a CMS <code>EnvelopedDataStream</code> message.
153 * <p>
154 *
155 * @param message the message to be enveloped, as byte representation
156 * @param contentEA the content encryption algorithm
157 * @param keyLength the key length for the symmetric key
158 * @return the DER encoding of the <code>EnvelopedData</code> object just created
159 * @throws Exception if the <code>EnvelopedData</code> object cannot
160 * be created
161 */
162 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
163
164 EnvelopedDataStream enveloped_data;
165
166 // we are testing the stream interface
167 ByteArrayInputStream is = new ByteArrayInputStream(message);
168 // create a new EnvelopedData object
169 try {
170 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength);
171 } catch (NoSuchAlgorithmException ex) {
172 throw new CMSException("No implementation for contentEA.getAlgorithm().getName().");
173 }
174
175
176 // create the recipient infos
177 RecipientInfo[] recipients = new RecipientInfo[2];
178 // user1 is the first receiver
179 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
180 // user2 is the second receiver
181 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
182 // specify the recipients of the encrypted message
183 enveloped_data.setRecipientInfos(recipients);
184
185 // return the EnvelopedDate as DER encoded byte array with block size 2048
186 ByteArrayOutputStream os = new ByteArrayOutputStream();
187 enveloped_data.writeTo(os, 2048);
188 return os.toByteArray();
189 }
190
191
192
193 /**
194 * Creates a CMS <code>EnvelopedDataStream</code> message.
195 * <p>
196 * Keys and parameters, and EncryptedContentInfoStream are created outside
197 * the EnvelopedDataStream class.
198 *
199 * @param message the message to be enveloped, as byte representation
200 * @param cea the content encryption algorithm
201 * @param keyLength the key length for the symmetric key
202 * @return the DER encoding of the <code>EnvelopedData</code> object just created
203 * @throws Exception if the <code>EnvelopedData</code> object cannot
204 * be created
205 */
206 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception {
207
208 AlgorithmID contentEA = (AlgorithmID)cea.clone();
209 ByteArrayInputStream is = new ByteArrayInputStream(message);
210
211 // generate parameters (iv and key length)
212 // create iv
213 byte[] iv = new byte[8];
214 random.nextBytes(iv);
215 SEQUENCE parameter = new SEQUENCE();
216 parameter.addComponent(new OCTET_STRING(iv));
217 parameter.addComponent(new INTEGER(keyLength));
218 contentEA.setParameter(parameter);
219 AlgorithmParameterSpec params = new IvParameterSpec(iv);
220
221 // generate the content encryption key
222 KeyGenerator keyGen = SecurityProvider.getSecurityProvider().getKeyGenerator(contentEA, keyLength);
223 // generate a new key
224 SecretKey secretKey = keyGen.generateKey();
225
226 // create the EncryptedContentInfo for the content to be encrypted
227 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is);
228 // setup the cipher for encryption
229 eci.setupCipher(contentEA, secretKey, params);
230
231 // create the recipient infos
232 RecipientInfo[] recipients = new RecipientInfo[2];
233 // user1 is the first receiver
234 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
235 // encrypt the secret key for recipient 1
236 recipients[0].encryptKey(secretKey);
237 // user2 is the second receiver
238 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
239 // encrypt the secret key for recipient 2
240 recipients[1].encryptKey(secretKey);
241 // now create the EnvelopedDataStream
242 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci);
243
244 // return the EnvelopedDate as DER encoded byte array with block size 2048
245 ByteArrayOutputStream os = new ByteArrayOutputStream();
246 enveloped_data.writeTo(os, 2048);
247 byte[] enc = os.toByteArray();
248 return enc;
249 }
250
251 /**
252 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
253 * specified recipient and returns the decrypted (= original) message.
254 * <p>
255 * Decryption and cipher setup and EncryptedContentInfoStrean processing
256 * is performed outside class EnvelopedDataStream.
257 *
258 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
259 * @param privateKey the private key to decrypt the message
260 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
261 * to which the specified private key belongs
262 *
263 * @return the recovered message, as byte array
264 * @throws CMSException if the message cannot be recovered
265 */
266 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
267
268 // create the EnvelopedData object from a DER encoded byte array
269 // we are testing the stream interface
270 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
271 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
272
273 // get the recipient infos
274 RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
275
276 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
277
278 for (int i=0; i<recipients.length; i++) {
279 System.out.println("Recipient "+(i+1)+":");
280 System.out.println(recipients[i].getRecipientIdentifiers()[0]);
281 }
282 // decrypt symmetric content encryption key, e.g.:
283 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk);
284
285 //get the ECI from the enveloped data:
286 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
287 System.out.println("\nContent type of encrypted data: " + eci.getContentType());
288 //get the content encryption algorithm:
289 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
290 System.out.println("Content Encryption Algorithm: " + contentEA);
291
292 // get the parameters as SEQUENCE
293 SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
294 // the iv is the first component
295 OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(0);
296 AlgorithmParameterSpec params = new IvParameterSpec((byte[])oct.getValue());
297
298 //now setup the cipher with previously decrypted recipient key amd params
299 eci.setupCipher(secretKey, params);
300 //get and read the data thereby actually performing the decryption
301 InputStream data_is = eci.getInputStream();
302 ByteArrayOutputStream baos = new ByteArrayOutputStream();
303 Util.copyStream(data_is, baos, null);
304 byte[] decrypted = baos.toByteArray();
305 return decrypted;
306 }
307
308 /**
309 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
310 * specified recipient and returns the decrypted (= original) message.
311 *
312 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
313 * @param privateKey the private key to decrypt the message
314 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
315 * to which the specified private key belongs
316 *
317 * @return the recovered message, as byte array
318 * @throws Exception if the message cannot be recovered
319 */
320 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
321
322 // create the EnvelopedData object from a DER encoded byte array
323 // we are testing the stream interface
324 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
325 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
326
327 // get the recipient infos
328 RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
329
330 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
331
332 for (int i=0; i<recipients.length; i++) {
333 System.out.println("Recipient "+(i+1)+": ");
334 System.out.println(recipients[i].getRecipientIdentifiers()[0]);
335 }
336
337 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo();
338 System.out.println("\nContent type of encrypted data: " + eci.getContentType());
339 //get the content encryption algorithm:
340 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
341 System.out.println("Content Encryption Algorithm: " + contentEA);
342 //now setup the cipher with previously decrypted recipient key amd params
343 enveloped_data.setupCipher(privateKey, recipientInfoIndex);
344 //get and read the data thereby actually performing the decryption
345 InputStream data_is = enveloped_data.getInputStream();
346 ByteArrayOutputStream baos = new ByteArrayOutputStream();
347 Util.copyStream(data_is, baos, null);
348 byte[] decrypted = baos.toByteArray();
349 return decrypted;
350
351 }
352
353
354
355 /**
356 * Starts the test.
357 */
358 public void start() {
359 // the test message
360 String m = "This is the test message.";
361 System.out.println("Test message: "+m);
362 System.out.println();
363 byte[] message = m.getBytes();
364
365 try {
366 byte[] data;
367 byte[] received_message = null;
368
369
370 // the stream implementation
371 //
372 // test CMS EnvelopedDataStream
373 //
374
375 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
376 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
377
378 // transmit data
379 System.out.println("\nEnvelopedDataStream demo [parse]:\n");
380 // user1 means index 0 (hardcoded for this demo)
381 received_message = getEnvelopedDataStream(data, user1_pk, 0);
382 //received_message = getEnvelopedDataStream(data, user2_pk, 1);
383 System.out.print("\nDecrypted content: ");
384 System.out.println(new String(received_message));
385
386 // test against EncryptedContentInfoStream - EnvelopedDataStream creation
387
388 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
389
390 System.out.println("Create EncryptedContentInfo for EnvelopedData...");
391 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
392 // transmit data
393 System.out.println("\nEnvelopedDataStream demo [parse]:\n");
394 // user1 means index 0 (hardcoded for this demo)
395 received_message = getEnvelopedDataStream(data, user1_pk, 0);
396 System.out.print("\nDecrypted content: ");
397 System.out.println(new String(received_message));
398
399 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
400 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
401 // transmit data
402
403 System.out.println("\nEnvelopedDataStream demo [parse]:\n");
404 // user1 means index 0 (hardcoded for this demo)
405 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData...");
406 received_message = getEncryptedContentInfoStream(data, user1_pk, 0);
407 System.out.print("\nDecrypted content: ");
408 System.out.println(new String(received_message));
409
410
411 } catch (Exception ex) {
412 ex.printStackTrace();
413 throw new RuntimeException(ex.toString());
414 }
415 }
416
417 /**
418 * The main method.
419 *
420 * @throws Exception
421 * if some error occurs
422 */
423 public static void main(String argv[]) throws Exception {
424
425 demo.DemoUtil.initDemos();
426
427 (new CAST128EnvelopedDataDemo()).start();
428 System.out.println("\nReady!");
429 DemoUtil.waitKey();
430 }
431 }