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    }