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    }