001// Copyright (C) 2002 IAIK
002// https://sic.tech/
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// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/cms/envelopedData/CAST128EnvelopedDataDemo.java 21    12.02.25 17:58 Dbratko $
029// $Revision: 21 $
030//
031
032package demo.cms.envelopedData;
033
034import iaik.asn1.INTEGER;
035import iaik.asn1.OCTET_STRING;
036import iaik.asn1.ObjectID;
037import iaik.asn1.SEQUENCE;
038import iaik.asn1.structures.AlgorithmID;
039import iaik.cms.CMSException;
040import iaik.cms.EncryptedContentInfoStream;
041import iaik.cms.EnvelopedDataStream;
042import iaik.cms.KeyTransRecipientInfo;
043import iaik.cms.RecipientInfo;
044import iaik.cms.SecurityProvider;
045import iaik.security.random.SecRandom;
046import iaik.utils.Util;
047import iaik.x509.X509Certificate;
048
049import java.io.ByteArrayInputStream;
050import java.io.ByteArrayOutputStream;
051import java.io.IOException;
052import java.io.InputStream;
053import java.security.NoSuchAlgorithmException;
054import java.security.PrivateKey;
055import java.security.SecureRandom;
056import java.security.spec.AlgorithmParameterSpec;
057
058import javax.crypto.KeyGenerator;
059import javax.crypto.SecretKey;
060import javax.crypto.spec.IvParameterSpec;
061
062import demo.DemoUtil;
063import demo.keystore.CMSKeyStore;
064
065/**
066 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages
067 * for the CAST128 algorithm.
068 * <p>
069 * This demo compares the usage of class EnvelopedDataStream for encrypting the content
070 * using CAST128 with automatical (transparent) key/parameter handling against explicit
071 * key/parameter/EncrypedContentInfoStream handling.
072 * <p>
073 * All keys and certificates are read from a keystore created by the
074 * SetupCMSKeyStore program.
075 * <p>
076 * CAST parameters are defined as:
077 * <pre>
078 * Parameters ::=  SEQUENCE {
079 *   iv         OCTET STRING DEFAULT 0,
080 *   keyLength  INTEGER }
081 * </pre>
082 */
083public class CAST128EnvelopedDataDemo {
084
085  // certificate of user 1
086  X509Certificate user1;
087  // private key of user 1
088  PrivateKey user1_pk;
089  // certificate of user 2
090  X509Certificate user2;
091  // private key of user 2
092  PrivateKey user2_pk;
093  // secure random number generator
094  SecureRandom random;
095  
096  /**
097   * Setup the demo certificate chains.
098   *
099   * Keys and certificate are retrieved from the demo KeyStore.
100   *
101   * @throws IOException if an file read error occurs
102   */
103  public CAST128EnvelopedDataDemo() throws IOException {
104    
105    System.out.println();
106    System.out.println("**********************************************************************************");
107    System.out.println("*                          CAST128EnvelopedDataDemo                              *");
108    System.out.println("*      (shows the usage of the EnvelopedData type for the CAST128 cipher)        *");
109    System.out.println("**********************************************************************************");
110    System.out.println();
111    // add all certificates to the list
112    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
113    user1 = certs[0];
114    user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
115    user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
116    user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
117    
118    random = SecRandom.getDefault();
119  }
120  
121  /**
122   * Creates a CMS <code>EnvelopedDataStream</code> message.
123   * <p>
124   *
125   * @param message the message to be enveloped, as byte representation
126   * @param contentEA the content encryption algorithm
127   * @param keyLength the key length for the symmetric key
128   * @return the DER encoding of the <code>EnvelopedData</code> object just created
129   * @throws Exception if the <code>EnvelopedData</code> object cannot
130   *                          be created
131   */
132  public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
133
134    EnvelopedDataStream enveloped_data;
135
136    // we are testing the stream interface
137    ByteArrayInputStream is = new ByteArrayInputStream(message);
138    // create a new EnvelopedData object 
139    try {
140      enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength);
141    } catch (NoSuchAlgorithmException ex) {
142      throw new CMSException("No implementation for contentEA.getAlgorithm().getName().");
143    }
144
145
146    // create the recipient infos
147    RecipientInfo[] recipients = new RecipientInfo[2];
148    // user1 is the first receiver
149    recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
150    // user2 is the second receiver
151    recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
152    // specify the recipients of the encrypted message
153    enveloped_data.setRecipientInfos(recipients);
154
155    // return the EnvelopedDate as DER encoded byte array with block size 2048
156    ByteArrayOutputStream os = new ByteArrayOutputStream();
157    enveloped_data.writeTo(os, 2048);
158    return os.toByteArray();
159  }
160
161
162
163  /**
164   * Creates a CMS <code>EnvelopedDataStream</code> message.
165   * <p>
166   * Keys and parameters, and EncryptedContentInfoStream are created outside
167   * the EnvelopedDataStream class.
168   *
169   * @param message the message to be enveloped, as byte representation
170   * @param cea the content encryption algorithm
171   * @param keyLength the key length for the symmetric key
172   * @return the DER encoding of the <code>EnvelopedData</code> object just created
173   * @throws Exception if the <code>EnvelopedData</code> object cannot
174   *                          be created
175   */
176  public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception {
177       
178    AlgorithmID contentEA = (AlgorithmID)cea.clone(); 
179    ByteArrayInputStream is = new ByteArrayInputStream(message);
180    
181    // generate parameters (iv and key length)
182    // create iv
183    byte[] iv = new byte[8];
184    random.nextBytes(iv);
185    SEQUENCE parameter = new SEQUENCE();
186    parameter.addComponent(new OCTET_STRING(iv));
187    parameter.addComponent(new INTEGER(keyLength));
188    contentEA.setParameter(parameter);
189    AlgorithmParameterSpec params = new IvParameterSpec(iv);
190
191    // generate the content encryption key
192    KeyGenerator keyGen = SecurityProvider.getSecurityProvider().getKeyGenerator(contentEA, keyLength);
193    // generate a new key
194    SecretKey secretKey = keyGen.generateKey();
195
196    // create the EncryptedContentInfo for the content to be encrypted
197    EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is);
198    // setup the cipher for encryption
199    eci.setupCipher(contentEA, secretKey, params);
200
201    // create the recipient infos
202    RecipientInfo[] recipients = new RecipientInfo[2];
203    // user1 is the first receiver
204    recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
205    // encrypt the secret key for recipient 1
206    recipients[0].encryptKey(secretKey);
207    // user2 is the second receiver
208    recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
209    // encrypt the secret key for recipient 2
210    recipients[1].encryptKey(secretKey);
211    // now create the EnvelopedDataStream
212    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci);
213
214    // return the EnvelopedDate as DER encoded byte array with block size 2048
215    ByteArrayOutputStream os = new ByteArrayOutputStream();
216    enveloped_data.writeTo(os, 2048);
217    byte[] enc = os.toByteArray();
218    return enc;
219  }
220
221  /**
222   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
223   * specified recipient and returns the decrypted (= original) message.
224   * <p>
225   * Decryption and cipher setup and EncryptedContentInfoStrean processing 
226   * is performed outside class EnvelopedDataStream.
227   *
228   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
229   * @param privateKey the private key to decrypt the message
230   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
231   *                           to which the specified private key belongs
232   *
233   * @return the recovered message, as byte array
234   * @throws CMSException if the message cannot be recovered
235   */
236  public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
237
238    // create the EnvelopedData object from a DER encoded byte array
239    // we are testing the stream interface
240    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
241    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
242
243    // get the recipient infos
244    RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
245
246    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
247
248    for (int i=0; i<recipients.length; i++) {
249      System.out.println("Recipient "+(i+1)+":");
250      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
251    }
252    // decrypt symmetric content encryption key, e.g.:
253    SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk);
254
255    //get the ECI from the enveloped data:
256    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
257    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
258    //get the content encryption algorithm:
259    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
260    System.out.println("Content Encryption Algorithm: " + contentEA);
261    
262    // get the parameters as SEQUENCE
263    SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
264    // the iv is the first component
265    OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(0);
266    AlgorithmParameterSpec params = new IvParameterSpec((byte[])oct.getValue());
267
268    //now setup the cipher with previously decrypted recipient key amd params
269    eci.setupCipher(secretKey, params);
270    //get and read the data thereby actually performing the decryption
271    InputStream data_is = eci.getInputStream();
272    ByteArrayOutputStream baos = new ByteArrayOutputStream();
273    Util.copyStream(data_is, baos, null);
274    byte[] decrypted = baos.toByteArray();
275    return decrypted;
276  }
277  
278  /**
279   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
280   * specified recipient and returns the decrypted (= original) message.
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[] getEnvelopedDataStream(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    
307    EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo();
308    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
309    //get the content encryption algorithm:
310    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
311    System.out.println("Content Encryption Algorithm: " + contentEA);
312    //now setup the cipher with previously decrypted recipient key amd params
313    enveloped_data.setupCipher(privateKey, recipientInfoIndex);
314    //get and read the data thereby actually performing the decryption
315    InputStream data_is = enveloped_data.getInputStream();
316    ByteArrayOutputStream baos = new ByteArrayOutputStream();
317    Util.copyStream(data_is, baos, null);
318    byte[] decrypted = baos.toByteArray();
319    return decrypted;
320
321  }
322
323  
324  
325  /**
326   * Starts the test.
327   */
328  public void start() {
329     // the test message
330    String m = "This is the test message.";
331    System.out.println("Test message: "+m);
332    System.out.println();
333    byte[] message = m.getBytes();
334
335    try {
336      byte[] data;
337      byte[] received_message = null;
338
339
340      // the stream implementation
341      //
342      // test CMS EnvelopedDataStream
343      //
344
345      System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
346      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
347
348      // transmit data
349      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
350      // user1 means index 0 (hardcoded for this demo)
351      received_message = getEnvelopedDataStream(data, user1_pk, 0);
352      //received_message = getEnvelopedDataStream(data, user2_pk, 1);
353      System.out.print("\nDecrypted content: ");
354      System.out.println(new String(received_message));
355
356      // test against EncryptedContentInfoStream - EnvelopedDataStream creation
357
358      System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
359      
360      System.out.println("Create EncryptedContentInfo for EnvelopedData...");
361      data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
362      // transmit data
363      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
364      // user1 means index 0 (hardcoded for this demo)
365      received_message = getEnvelopedDataStream(data, user1_pk, 0);
366      System.out.print("\nDecrypted content: ");
367      System.out.println(new String(received_message));
368      
369      System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n");
370      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128);
371      // transmit data
372   
373      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
374      // user1 means index 0 (hardcoded for this demo)
375      System.out.println("Decrypt EncryptedContentInfo of EnvelopedData...");
376      received_message = getEncryptedContentInfoStream(data, user1_pk, 0);
377      System.out.print("\nDecrypted content: ");
378      System.out.println(new String(received_message));
379     
380  
381        } catch (Exception ex) {
382          ex.printStackTrace();
383          throw new RuntimeException(ex.toString());
384        }
385  }
386
387  /**
388   * The main method.
389   *
390   * @throws Exception
391   *            if some error occurs
392   */
393  public static void main(String argv[]) throws Exception {
394
395        demo.DemoUtil.initDemos();
396
397    (new CAST128EnvelopedDataDemo()).start();
398    System.out.println("\nReady!");
399    DemoUtil.waitKey();
400  }
401}