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