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    }