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