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/EncryptedContentInfoDemo.java 26    12.02.25 17:58 Dbratko $
059    // $Revision: 26 $
060    //
061    
062    package demo.cms.envelopedData;
063    
064    import iaik.asn1.ASN;
065    import iaik.asn1.ASN1Object;
066    import iaik.asn1.INTEGER;
067    import iaik.asn1.OCTET_STRING;
068    import iaik.asn1.ObjectID;
069    import iaik.asn1.SEQUENCE;
070    import iaik.asn1.structures.AlgorithmID;
071    import iaik.cms.EncryptedContentInfoStream;
072    import iaik.cms.EnvelopedDataStream;
073    import iaik.cms.KeyTransRecipientInfo;
074    import iaik.cms.RecipientInfo;
075    import iaik.cms.SecurityProvider;
076    import iaik.security.random.SecRandom;
077    import iaik.utils.Util;
078    import iaik.x509.X509Certificate;
079    
080    import java.io.ByteArrayInputStream;
081    import java.io.ByteArrayOutputStream;
082    import java.io.IOException;
083    import java.io.InputStream;
084    import java.math.BigInteger;
085    import java.security.PrivateKey;
086    import java.security.SecureRandom;
087    import java.security.spec.AlgorithmParameterSpec;
088    
089    import javax.crypto.KeyGenerator;
090    import javax.crypto.SecretKey;
091    import javax.crypto.spec.IvParameterSpec;
092    import javax.crypto.spec.RC2ParameterSpec;
093    
094    import demo.keystore.CMSKeyStore;
095    
096    /**
097     * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages
098     * for algorithms that require a specific parameter handling.
099     * <p>
100     * All keys and certificates are read from a keystore created by the
101     * SetupCMSKeyStore program.
102     * <p>
103     * The following algorithms are demonstrated:
104     * <ul>
105     * <li>ARCFOUR: Variable-key-size stream cipher; no parameters to be sent
106     * <li>RC2_CBC: Variable-key-size block cipher; parameters as used by S/MIME:
107     *              rc2ParamterVersion and IV; encoded as SEQUENCE:
108     *              <pre>
109     *               RC2-CBC parameter ::=  SEQUENCE {
110     *                 rc2ParameterVersion  INTEGER,
111     *                 iv                   OCTET STRING (8)}
112     *
113     *               For the effective-key-bits of 40, 64, and 128, the
114     *                rc2ParameterVersion values are 160, 120, 58 respectively.
115     *              </pre>
116     * <li>CAST5_CBC: Feistel type block cipher with key sizes of 40-128 bit in 8 bit
117     *              increments; parameters (RFC 2144):
118     *              <pre>
119     *               Parameters ::=  SEQUENCE {
120     *                 iv         OCTET STRING DEFAULT 0,
121     *                 keyLength  INTEGER }
122     *
123     *              </pre>
124     * </ul>
125     * This class shows how an EncryptedContentInfo is explicit created for encrypting
126     * the content and supplying it to an EnvelopedDataStream object. Note that IAIK-CMS
127     * also allows to use EnvelopedData(Stream) for algorithms like RC2, ARCFOUR or CAST 
128     * without having the necessity of explicit key/parameter handling, see {@link 
129     * RC2EnvelopedDataDemo RC2EnvelopedDataDemo} for an example. Note that the usage
130     * of algorithms like RC2 is deprecated but used here for this demo since it
131     * requires a specific parameter handling.
132     */
133    public class EncryptedContentInfoDemo {
134    
135      // certificate of user 1
136      X509Certificate user1;
137      // private key of user 1
138      PrivateKey user1_pk;
139      // certificate of user 2
140      X509Certificate user2;
141      // private key of user 2
142      PrivateKey user2_pk;
143      // secure random number generator
144      SecureRandom random;
145    
146      /**
147       * Setup the demo certificate chains.
148       *
149       * Keys and certificate are retrieved from the demo KeyStore.
150       *
151       * @throws IOException if an file read error occurs
152       */
153      public EncryptedContentInfoDemo() throws IOException {
154        
155        System.out.println();
156        System.out.println("********************************************************************************************");
157        System.out.println("*                               EncryptedContentInfoDemo                                   *");
158        System.out.println("* (shows the usage of the EncryptedContentInfo implementation for encrypting some message) *");
159        System.out.println("********************************************************************************************");
160        System.out.println();
161        
162        
163        // add all certificates to the list
164        X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
165        user1 = certs[0];
166        user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
167        user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
168        user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
169    
170        random = SecRandom.getDefault();
171      }
172    
173    
174      /**
175       * Creates a CMS <code>EnvelopedDataStream</code> message.
176       *
177       * @param message the message to be enveloped, as byte representation
178       * @param contentEA the content encryption algorithm
179       * @param keyLength the key length for the symmetric key
180       * @return the DER encoding of the <code>EnvelopedData</code> object just created
181       * @throws Exception if the <code>EnvelopedData</code> object cannot be created
182       */
183      public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
184    
185          SecurityProvider provider = SecurityProvider.getSecurityProvider();
186          ByteArrayInputStream is = new ByteArrayInputStream(message);
187    
188          AlgorithmParameterSpec params = null;
189          SecretKey secretKey = null;
190    
191          // create iv
192          byte[] iv = new byte[8];
193          random.nextBytes(iv);
194    
195          int rc2_param = 58;
196          if (contentEA.equals(AlgorithmID.rc2_CBC)) {
197             
198             switch (keyLength) {
199               case 40:
200                 rc2_param = 160;
201                 break;
202               case 64:
203                 rc2_param = 120;
204                 break;
205               default:    // 128
206                 rc2_param = 58;
207                 keyLength = 128;
208             }
209             // create the paramters (SEQUENCE) to be sent
210             SEQUENCE parameter = new SEQUENCE();
211             parameter.addComponent(new INTEGER(rc2_param));
212             parameter.addComponent(new OCTET_STRING(iv));
213             contentEA.setParameter(parameter);
214             params = new RC2ParameterSpec(keyLength,iv);
215          }  else if (contentEA.equals(AlgorithmID.arcfour)){
216             // no params for ARCFOUR
217             params = null;
218          } else if (contentEA.equals(AlgorithmID.cast5_CBC)) {
219             SEQUENCE parameter = new SEQUENCE();
220             parameter.addComponent(new OCTET_STRING(iv));
221             parameter.addComponent(new INTEGER(keyLength));
222             contentEA.setParameter(parameter);
223             params = new IvParameterSpec(iv);
224    
225          } else {
226             throw new Exception("Algorithm " + contentEA + " not supportted for this test!");
227          }
228    
229          KeyGenerator keyGen = provider.getKeyGenerator(contentEA, keyLength);
230          // generate a new key
231          secretKey = keyGen.generateKey();
232    
233          // create the EncryptedContentInfo for the content to be encrypted
234          EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.pkcs7_data, is);
235          // setup the cipher for encryption
236          eci.setupCipher(contentEA, secretKey, params);
237    
238           // create the recipient infos
239          RecipientInfo[] recipients = new RecipientInfo[2];
240          // user1 is the first receiver
241          recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
242          // encrypt the secret key for recipient 1
243          recipients[0].encryptKey(secretKey);
244          // user2 is the second receiver
245          recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
246          // encrypt the secret key for recipient 2
247          recipients[1].encryptKey(secretKey);
248          // now create the EnvelopedDataStream
249          EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci);
250    
251          // return the EnvelopedDate as DER encoded byte array with block size 2048
252          ByteArrayOutputStream os = new ByteArrayOutputStream();
253          enveloped_data.writeTo(os, 2048);
254          byte[] enc = os.toByteArray();
255          return enc;
256    
257      }
258    
259      /**
260       * Decrypts the encrypted content of the given CMS <code>EnvelopedData</code> object for the
261       * specified recipient and returns the decrypted (= original) message.
262       *
263       * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
264       * @param privateKey the private key to decrypt the message
265       * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
266       *                           to which the specified private key belongs
267       *
268       * @return the recovered message, as byte array
269       * @throws Exception if the message cannot be recovered
270       */
271      public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
272    
273        // create the EnvelopedData object from a DER encoded byte array
274        // we are testing the stream interface
275        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
276        EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
277    
278        AlgorithmParameterSpec params = null;
279        // get the recipient infos
280        RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
281    
282        System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
283    
284        for (int i=0; i<recipients.length; i++) {
285          System.out.println("Recipient "+(i+1)+":");
286          System.out.println(recipients[i].getRecipientIdentifiers()[0]);
287        }
288        // decrypt symmetric content encryption key, e.g.:
289        SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk);
290    
291        //get the ECI from the enveloped data:
292        EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
293        //get the content encryption algorithm:
294        AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
295        System.out.println("\nContent Encryption Algorithm: " + contentEA);
296        if (contentEA.equals(AlgorithmID.rc2_CBC)) {
297            // get the parameters as SEQUENCE
298            SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
299            // the iv is the second component
300            OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(1);
301            // create an IvParameterSpec:
302            //params = new IvParameterSpec((byte[])oct.getValue());
303            int rc2ParameterVersion = ((BigInteger)seq.getComponentAt(0).getValue()).intValue();
304            int effective_key_bits = 32;
305            switch (rc2ParameterVersion) {
306                  case 160:
307                    effective_key_bits = 40;
308                    break;
309                  case 120:
310                    effective_key_bits = 64;
311                    break;
312                  case 58:
313                    effective_key_bits = 128;
314                    break;
315                  default:
316                    throw new Exception("Invalid rc2ParameterVersion " + rc2ParameterVersion + "!");
317    
318                 }
319                 params = new RC2ParameterSpec(effective_key_bits,(byte[])seq.getComponentAt(1).getValue());
320    
321          }
322          else if (contentEA.equals(AlgorithmID.rc5_CBC)) {
323             OCTET_STRING oct = (OCTET_STRING)contentEA.getParameter();
324             // create an IvParameterSpec:
325             params = new IvParameterSpec((byte[])oct.getValue());
326          } else if (contentEA.equals(AlgorithmID.arcfour)) {
327             params = null;
328          } else if (contentEA.equals(AlgorithmID.cast5_CBC)) {
329              // get the parameters
330              ASN1Object asn1Params = contentEA.getParameter();
331              if (asn1Params.isA(ASN.SEQUENCE)) {
332                // the iv is the first component
333                params = new IvParameterSpec((byte[])asn1Params.getComponentAt(0).getValue());
334              } else {
335                // to be compatible with (invalid) CAST AlgorithmIDs only using the IV as parameters
336                params = new IvParameterSpec((byte[])asn1Params.getValue());
337              }  
338          } else {
339             throw new Exception("Algorithm " + contentEA + " not supportted for this test!");
340          }
341    
342    
343          //now setup the cipher with previously decrypted recipient key amd params
344          eci.setupCipher(secretKey, params);
345          //get and read the data thereby actually performing the decryption
346          InputStream data_is = eci.getInputStream();
347          ByteArrayOutputStream baos = new ByteArrayOutputStream();
348          Util.copyStream(data_is, baos, null);
349          byte[] decrypted = baos.toByteArray();
350          return decrypted;
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          // ARCFOUR
376          System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n");
377          data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128);
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          System.out.print("\nDecrypted content: ");
383          System.out.println(new String(received_message));
384    
385          // RC2
386          System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n");
387          data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128);
388          // transmit data
389          System.out.println("\nEnvelopedDataStream demo [parse]:\n");
390          // user1 means index 0 (hardcoded for this demo)
391          received_message = getEnvelopedDataStream(data, user1_pk, 0);
392          System.out.print("\nDecrypted content: ");
393          System.out.println(new String(received_message));
394    
395          // CAST5_CBC
396          System.out.println("\nEnvelopedDataStream demo for algorithm CAST5_CBC [create]:\n");
397          data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
398          // transmit data
399          System.out.println("\nEnvelopedDataStream demo [parse]:\n");
400          // user1 means index 0 (hardcoded for this demo)
401          received_message = getEnvelopedDataStream(data, user1_pk, 0);
402          System.out.print("\nDecrypted content: ");
403          System.out.println(new String(received_message));
404          
405            } catch (Exception ex) {
406              ex.printStackTrace();
407              throw new RuntimeException(ex.toString());
408            }
409      }
410    
411      /**
412       * The main method.
413       *
414       * @throws IOException
415       *            if an I/O error occurs when reading required keys
416       *            and certificates from files
417       */
418      public static void main(String argv[]) throws Exception {
419    
420            demo.DemoUtil.initDemos();
421    
422        (new EncryptedContentInfoDemo()).start();
423        System.out.println("\nReady!");
424        System.in.read();
425      }
426    }