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/RC2EnvelopedDataDemo.java 22    12.02.25 17:58 Dbratko $
029// $Revision: 22 $
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.security.random.SecRandom;
045import iaik.utils.Util;
046import iaik.x509.X509Certificate;
047
048import java.io.ByteArrayInputStream;
049import java.io.ByteArrayOutputStream;
050import java.io.IOException;
051import java.io.InputStream;
052import java.math.BigInteger;
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.RC2ParameterSpec;
061
062import demo.DemoUtil;
063import demo.keystore.CMSKeyStore;
064
065/**
066 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages
067 * for the RC2 algorithm.
068 * <p>
069 * This demo compares the usage of class EnvelopedDataStream for encrypting the content
070 * using RC2 with automatic (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 * RC2 parameters are defined as:
077 * <ul>
078 * <li>RC2_CBC: Variable-key-size block cipher; parameters as used by S/MIME:
079 *              rc2ParamterVersion and IV; encoded as SEQUENCE:
080 *              <pre>
081 *               RC2-CBC parameter ::=  SEQUENCE {
082 *                 rc2ParameterVersion  INTEGER,
083 *                 iv                   OCTET STRING (8)}
084 *
085 *               For the effective-key-bits of 40, 64, and 128, the
086 *                rc2ParameterVersion values are 160, 120, 58 respectively.
087 *              </pre>
088 * </ul>
089 * 
090 * Note that the usage of RC2 is deprecated but used here for this demo since it
091 * requires a specific parameter handling.
092 * 
093 */
094public class RC2EnvelopedDataDemo {
095
096  // certificate of user 1
097  X509Certificate user1;
098  // private key of user 1
099  PrivateKey user1_pk;
100  // certificate of user 2
101  X509Certificate user2;
102  // private key of user 2
103  PrivateKey user2_pk;
104  // secure random number generator
105  SecureRandom random;
106
107  /**
108   * Setup the demo certificate chains.
109   *
110   * Keys and certificate are retrieved from the demo KeyStore.
111   *
112   * @throws IOException if an file read error occurs
113   */
114  public RC2EnvelopedDataDemo() throws IOException {
115    
116    System.out.println();
117    System.out.println("****************************************************************************************");
118    System.out.println("*                           RC2EnvelopedDataDemo                                       *");
119    System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for the RC2 algorithm) *");
120    System.out.println("****************************************************************************************");
121    System.out.println();
122    
123    // add all certificates to the list
124    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
125    user1 = certs[0];
126    user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
127    user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
128    user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
129    
130    random = SecRandom.getDefault();
131  }
132  
133  /**
134   * Creates a CMS <code>EnvelopedDataStream</code> message.
135   * <p>
136   *
137   * @param message the message to be enveloped, as byte representation
138   * @param contentEA the content encryption algorithm
139   * @param keyLength the key length for the symmetric key
140   * @return the DER encoding of the <code>EnvelopedData</code> object just created
141   * @throws CMSException if the <code>EnvelopedData</code> object cannot
142   *                          be created
143   */
144  public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
145
146    EnvelopedDataStream enveloped_data;
147
148    // we are testing the stream interface
149    ByteArrayInputStream is = new ByteArrayInputStream(message);
150    // create a new EnvelopedData object
151    try {
152      enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength);
153    } catch (NoSuchAlgorithmException ex) {
154      throw new CMSException("No implementation for contentEA.getAlgorithm().getName().");
155    }
156
157    // create the recipient infos
158    RecipientInfo[] recipients = new RecipientInfo[2];
159    // user1 is the first receiver
160    recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
161    // user2 is the second receiver
162    recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
163    // specify the recipients of the encrypted message
164    enveloped_data.setRecipientInfos(recipients);
165
166    // return the EnvelopedDate as DER encoded byte array with block size 2048
167    ByteArrayOutputStream os = new ByteArrayOutputStream();
168    enveloped_data.writeTo(os, 2048);
169    return os.toByteArray();
170  }
171
172
173
174  /**
175   * Creates a CMS <code>EnvelopedDataStream</code> message.
176   * <p>
177   * Keys and parameters, and EncryptedContentInfoStream are created outside
178   * the EnvelopedDataStream class.
179   *
180   * @param message the message to be enveloped, as byte representation
181   * @param contentEA the content encryption algorithm
182   * @param keyLength the key length for the symmetric key
183   * @return the DER encoding of the <code>EnvelopedData</code> object just created
184   * @throws Exception if the <code>EnvelopedData</code> object cannot
185   *                          be created
186   */
187  public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
188       
189      ByteArrayInputStream is = new ByteArrayInputStream(message);
190      
191      // parameters: iv and rc2 parameter version
192      // create iv
193      byte[] iv = new byte[8];
194      random.nextBytes(iv);
195      int rc2_param = 58;
196      switch (keyLength) {
197        case 40:
198          rc2_param = 160;
199          break;
200        case 64:
201          rc2_param = 120;
202          break;
203        default:    // 128
204          rc2_param = 58;
205          keyLength = 128;
206      }
207      // create the paramters (SEQUENCE) to be sent
208      SEQUENCE parameter = new SEQUENCE();
209      parameter.addComponent(new INTEGER(rc2_param));
210      parameter.addComponent(new OCTET_STRING(iv));
211      contentEA.setParameter(parameter);
212      AlgorithmParameterSpec params = new RC2ParameterSpec(keyLength,iv);
213      
214      // generate a new content encryption key
215      KeyGenerator key_gen = KeyGenerator.getInstance("RC2");
216      key_gen.init(keyLength);
217      SecretKey secretKey = key_gen.generateKey();
218
219      // create the EncryptedContentInfo for the content to be encrypted
220      EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is);
221      // setup the cipher for encryption
222      eci.setupCipher(contentEA, secretKey, params);
223
224      // create the recipient infos
225      RecipientInfo[] recipients = new RecipientInfo[2];
226      // user1 is the first receiver
227      recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
228      // encrypt the secret key for recipient 1
229      recipients[0].encryptKey(secretKey);
230      // user2 is the second receiver
231      recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
232      // encrypt the secret key for recipient 2
233      recipients[1].encryptKey(secretKey);
234      // now create the EnvelopedDataStream
235      EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci);
236
237      // return the EnvelopedDate as DER encoded byte array with block size 2048
238      ByteArrayOutputStream os = new ByteArrayOutputStream();
239      enveloped_data.writeTo(os, 2048);
240      byte[] enc = os.toByteArray();
241      return enc;
242
243  }
244
245  /**
246   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
247   * specified recipient and returns the decrypted (= original) message.
248   * <p>
249   * Decryption and cipher setup and EncryptedContentInfoStrean processing 
250   * is performed outside class EnvelopedDataStream.
251   *
252   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
253   * @param privateKey the private key to decrypt the message
254   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
255   *                           to which the specified private key belongs
256   *
257   * @return the recovered message, as byte array
258   * @throws Exception if the message cannot be recovered
259   */
260  public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
261
262    // create the EnvelopedData object from a DER encoded byte array
263    // we are testing the stream interface
264    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
265    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
266
267    // get the recipient infos
268    RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
269
270    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
271
272    for (int i=0; i<recipients.length; i++) {
273      System.out.println("Recipient "+(i+1)+":");
274      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
275    }
276    // decrypt symmetric content encryption key, e.g.:
277    SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk);
278
279    //get the ECI from the enveloped data:
280    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
281    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
282    //get the content encryption algorithm:
283    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
284    System.out.println("Content Encryption Algorithm: " + contentEA);
285    // get the parameters as SEQUENCE
286    SEQUENCE seq = (SEQUENCE)contentEA.getParameter();
287    // create an IvParameterSpec:
288    int rc2ParameterVersion = ((BigInteger)seq.getComponentAt(0).getValue()).intValue();
289    int effective_key_bits = 32;
290    switch (rc2ParameterVersion) {
291      case 160:
292        effective_key_bits = 40;
293        break;
294      case 120:
295        effective_key_bits = 64;
296        break;
297      case 58:
298        effective_key_bits = 128;
299        break;
300      default:
301        throw new Exception("Invalid rc2ParameterVersion " + rc2ParameterVersion + "!");
302      }
303    AlgorithmParameterSpec params = new RC2ParameterSpec(effective_key_bits,(byte[])seq.getComponentAt(1).getValue());
304
305    //now setup the cipher with previously decrypted recipient key amd params
306    eci.setupCipher(secretKey, params);
307    //get and read the data thereby actually performing the decryption
308    InputStream data_is = eci.getInputStream();
309    ByteArrayOutputStream baos = new ByteArrayOutputStream();
310    Util.copyStream(data_is, baos, null);
311    byte[] decrypted = baos.toByteArray();
312    return decrypted;
313  }
314  
315  /**
316   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
317   * specified recipient and returns the decrypted (= original) message.
318   *
319   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
320   * @param privateKey the private key to decrypt the message
321   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
322   *                           to which the specified private key belongs
323   *
324   * @return the recovered message, as byte array
325   * @throws Exception if the message cannot be recovered
326   */
327  public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
328
329    // create the EnvelopedData object from a DER encoded byte array
330    // we are testing the stream interface
331    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
332    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
333
334    // get the recipient infos
335    RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
336
337    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
338
339    for (int i=0; i<recipients.length; i++) {
340      System.out.println("Recipient "+(i+1)+": ");
341      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
342    }
343    
344    EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo();
345    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
346    //get the content encryption algorithm:
347    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
348    System.out.println("Content Encryption Algorithm: " + contentEA);
349    //now setup the cipher with previously decrypted recipient key amd params
350    enveloped_data.setupCipher(privateKey, recipientInfoIndex);
351    //get and read the data thereby actually performing the decryption
352    InputStream data_is = enveloped_data.getInputStream();
353    ByteArrayOutputStream baos = new ByteArrayOutputStream();
354    Util.copyStream(data_is, baos, null);
355    byte[] decrypted = baos.toByteArray();
356    return decrypted;
357
358  }
359
360  
361  
362  /**
363   * Starts the test.
364   */
365  public void start() {
366     // the test message
367    String m = "This is the test message.";
368    System.out.println("Test message: "+m);
369    System.out.println();
370    byte[] message = m.getBytes();
371
372    try {
373      byte[] data;
374      byte[] received_message = null;
375
376
377      // the stream implementation
378      //
379      // test CMS EnvelopedDataStream
380      //
381
382
383      // RC2
384      System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n");
385      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128);
386
387      // transmit data
388      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
389      // user1 means index 0 (hardcoded for this demo)
390      received_message = getEnvelopedDataStream(data, user1_pk, 0);
391      //received_message = getEnvelopedDataStream(data, user2_pk, 1);
392      System.out.print("\nDecrypted content: ");
393      System.out.println(new String(received_message));
394
395      // test against EncryptedContentInfoStream - EnvelopedDataStream creation
396
397      System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n");
398      
399      System.out.println("Create EncryptedContentInfo for EnvelopedData...");
400      data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128);
401      // transmit data
402      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
403      // user1 means index 0 (hardcoded for this demo)
404      received_message = getEnvelopedDataStream(data, user1_pk, 0);
405      System.out.print("\nDecrypted content: ");
406      System.out.println(new String(received_message));
407      
408      System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n");
409      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128);
410      // transmit data
411   
412      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
413      // user1 means index 0 (hardcoded for this demo)
414      System.out.println("Decrypt EncryptedContentInfo of EnvelopedData...");
415      received_message = getEncryptedContentInfoStream(data, user1_pk, 0);
416      System.out.print("\nDecrypted content: ");
417      System.out.println(new String(received_message));
418     
419  
420        } catch (Exception ex) {
421          ex.printStackTrace();
422          throw new RuntimeException(ex.toString());
423        }
424  }
425
426  /**
427   * The main method.
428   *
429   * @throws Exception
430   *            if some error occurs
431   */
432  public static void main(String argv[]) throws Exception {
433
434        demo.DemoUtil.initDemos();
435
436    (new RC2EnvelopedDataDemo()).start();
437    System.out.println("\nReady!");
438    DemoUtil.waitKey();
439  }
440}