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/ArcFourEnvelopedDataDemo.java 20    12.02.25 17:58 Dbratko $
029// $Revision: 20 $
030//
031
032package demo.cms.envelopedData;
033
034import iaik.asn1.ObjectID;
035import iaik.asn1.structures.AlgorithmID;
036import iaik.cms.CMSException;
037import iaik.cms.EncryptedContentInfoStream;
038import iaik.cms.EnvelopedDataStream;
039import iaik.cms.KeyTransRecipientInfo;
040import iaik.cms.RecipientInfo;
041import iaik.security.random.SecRandom;
042import iaik.utils.Util;
043import iaik.x509.X509Certificate;
044
045import java.io.ByteArrayInputStream;
046import java.io.ByteArrayOutputStream;
047import java.io.IOException;
048import java.io.InputStream;
049import java.security.NoSuchAlgorithmException;
050import java.security.PrivateKey;
051import java.security.SecureRandom;
052import java.security.spec.AlgorithmParameterSpec;
053
054import javax.crypto.KeyGenerator;
055import javax.crypto.SecretKey;
056
057import demo.keystore.CMSKeyStore;
058
059/**
060 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages
061 * for the ARCFOUR algorithm. The ARCFOUR stream cipher is believed to be compatible with 
062 * RC4[TM], a proprietary cipher of RSA Security Inc.. 
063 * <p>
064 * This demo compares the usage of class EnvelopedDataStream for encrypting the content
065 * using ARCFOUR with automatical (transparent) key/parameter handling against explicit
066 * key/parameter/EncrypedContentInfoStream handling.
067 * <p>
068 * All keys and certificates are read from a keystore created by the
069 * SetupCMSKeyStore program.
070 * <p>
071 * ARCFOUR requires no parameters.
072 */
073public class ArcFourEnvelopedDataDemo {
074
075  // certificate of user 1
076  X509Certificate user1;
077  // private key of user 1
078  PrivateKey user1_pk;
079  // certificate of user 2
080  X509Certificate user2;
081  // private key of user 2
082  PrivateKey user2_pk;
083  // secure random number generator
084  SecureRandom random;
085
086  /**
087   * Setup the demo certificate chains.
088   *
089   * Keys and certificate are retrieved from the demo KeyStore.
090   *
091   * @throws IOException if an file read error occurs
092   */
093  public ArcFourEnvelopedDataDemo() throws IOException {
094    
095    System.out.println();
096    System.out.println("********************************************************************************************");
097    System.out.println("*                             ArcFourEnvelopedDataDemo                                     *");
098    System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for the ARCFOUR algorithm) *");
099    System.out.println("********************************************************************************************");
100    System.out.println();
101    
102    // add all certificates to the list
103    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
104    user1 = certs[0];
105    user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
106    user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
107    user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
108    
109    random = SecRandom.getDefault();
110  }
111  
112  /**
113   * Creates a CMS <code>EnvelopedDataStream</code> message.
114   * <p>
115   *
116   * @param message the message to be enveloped, as byte representation
117   * @param contentEA the content encryption algorithm
118   * @param keyLength the key length for the symmetric key
119   * @return the DER encoding of the <code>EnvelopedData</code> object just created
120   * @throws CMSException if the <code>EnvelopedData</code> object cannot
121   *                          be created
122   */
123  public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception {
124
125    EnvelopedDataStream enveloped_data;
126
127    // we are testing the stream interface
128    ByteArrayInputStream is = new ByteArrayInputStream(message);
129    // create a new EnvelopedData object 
130    try {
131      enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength);
132    } catch (NoSuchAlgorithmException ex) {
133      throw new CMSException("No implementation for contentEA.getAlgorithm().getName().");
134    }
135
136
137    // create the recipient infos
138    RecipientInfo[] recipients = new RecipientInfo[2];
139    // user1 is the first receiver
140    recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
141    // user2 is the second receiver
142    recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
143    // specify the recipients of the encrypted message
144    enveloped_data.setRecipientInfos(recipients);
145
146    // return the EnvelopedDate as DER encoded byte array with block size 2048
147    ByteArrayOutputStream os = new ByteArrayOutputStream();
148    enveloped_data.writeTo(os, 2048);
149    return os.toByteArray();
150  }
151
152
153
154  /**
155   * Creates a CMS <code>EnvelopedDataStream</code> message.
156   * <p>
157   * Keys and parameters, and EncryptedContentInfoStream are created outside
158   * the EnvelopedDataStream class.
159   *
160   * @param message the message to be enveloped, as byte representation
161   * @param cea the content encryption algorithm
162   * @param keyLength the key length for the symmetric key
163   * @return the DER encoding of the <code>EnvelopedData</code> object just created
164   * @throws Exception if the <code>EnvelopedData</code> object cannot
165   *                          be created
166   */
167  public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception {
168       
169      AlgorithmID contentEA = (AlgorithmID)cea.clone(); 
170      ByteArrayInputStream is = new ByteArrayInputStream(message);
171      
172      // generate the content encryption key
173      KeyGenerator key_gen = KeyGenerator.getInstance("ARCFOUR");
174      key_gen.init(keyLength);
175      // generate a new key
176      SecretKey secretKey = key_gen.generateKey();
177
178      // create the EncryptedContentInfo for the content to be encrypted
179      EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is);
180      // setup the cipher for encryption
181      eci.setupCipher(contentEA, secretKey, (AlgorithmParameterSpec)null);
182
183       // create the recipient infos
184      RecipientInfo[] recipients = new RecipientInfo[2];
185      // user1 is the first receiver
186      recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
187      // encrypt the secret key for recipient 1
188      recipients[0].encryptKey(secretKey);
189      // user2 is the second receiver
190      recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
191      // encrypt the secret key for recipient 2
192      recipients[1].encryptKey(secretKey);
193      // now create the EnvelopedDataStream
194      EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci);
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      byte[] enc = os.toByteArray();
200      return enc;
201
202  }
203
204  /**
205   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
206   * specified recipient and returns the decrypted (= original) message.
207   * <p>
208   * Decryption and cipher setup and EncryptedContentInfoStrean processing 
209   * is performed outside class EnvelopedDataStream.
210   *
211   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
212   * @param privateKey the private key to decrypt the message
213   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
214   *                           to which the specified private key belongs
215   *
216   * @return the recovered message, as byte array
217   * @throws Exception if the message cannot be recovered
218   */
219  public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
220
221    // create the EnvelopedData object from a DER encoded byte array
222    // we are testing the stream interface
223    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
224    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
225
226    AlgorithmParameterSpec params = null;
227    // get the recipient infos
228    RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
229
230    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
231
232    for (int i=0; i<recipients.length; i++) {
233      System.out.println("Recipient "+(i+1)+":");
234      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
235    }
236    // decrypt symmetric content encryption key, e.g.:
237    SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk);
238
239    //get the ECI from the enveloped data:
240    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
241    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
242    //get the content encryption algorithm:
243    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
244    System.out.println("Content Encryption Algorithm: " + contentEA);
245    //now setup the cipher with previously decrypted recipient key amd params
246    eci.setupCipher(secretKey);
247    //get and read the data thereby actually performing the decryption
248    InputStream data_is = eci.getInputStream();
249    ByteArrayOutputStream baos = new ByteArrayOutputStream();
250    Util.copyStream(data_is, baos, null);
251    byte[] decrypted = baos.toByteArray();
252    return decrypted;
253  }
254  
255  /**
256   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
257   * specified recipient and returns the decrypted (= original) message.
258   *
259   * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
260   * @param privateKey the private key to decrypt the message
261   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
262   *                           to which the specified private key belongs
263   *
264   * @return the recovered message, as byte array
265   * @throws Exception if the message cannot be recovered
266   */
267  public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception {
268
269    // create the EnvelopedData object from a DER encoded byte array
270    // we are testing the stream interface
271    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
272    EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
273
274    // get the recipient infos
275    RecipientInfo[]  recipients = enveloped_data.getRecipientInfos();
276
277    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
278
279    for (int i=0; i<recipients.length; i++) {
280      System.out.println("Recipient "+(i+1)+": ");
281      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
282    }
283    
284    EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo();
285    System.out.println("\nContent type of encrypted data: " + eci.getContentType());
286    //get the content encryption algorithm:
287    AlgorithmID contentEA = eci.getContentEncryptionAlgorithm();
288    System.out.println("Content Encryption Algorithm: " + contentEA);
289    //now setup the cipher with previously decrypted recipient key amd params
290    enveloped_data.setupCipher(privateKey, recipientInfoIndex);
291    //get and read the data thereby actually performing the decryption
292    InputStream data_is = enveloped_data.getInputStream();
293    ByteArrayOutputStream baos = new ByteArrayOutputStream();
294    Util.copyStream(data_is, baos, null);
295    byte[] decrypted = baos.toByteArray();
296    return decrypted;
297
298  }
299
300  
301  
302  /**
303   * Starts the test.
304   */
305  public void start() {
306     // the test message
307    String m = "This is the test message.";
308    System.out.println("Test message: "+m);
309    System.out.println();
310    byte[] message = m.getBytes();
311
312    try {
313      byte[] data;
314      byte[] received_message = null;
315
316
317      // the stream implementation
318      //
319      // test CMS EnvelopedDataStream
320      //
321
322      System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n");
323      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128);
324
325      // transmit data
326      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
327      // user1 means index 0 (hardcoded for this demo)
328      received_message = getEnvelopedDataStream(data, user1_pk, 0);
329      //received_message = getEnvelopedDataStream(data, user2_pk, 1);
330      System.out.print("\nDecrypted content: ");
331      System.out.println(new String(received_message));
332
333      // test against EncryptedContentInfoStream - EnvelopedDataStream creation
334
335      System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n");
336      
337      System.out.println("Create EncryptedContentInfo for EnvelopedData...");
338      data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128);
339      // transmit data
340      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
341      // user1 means index 0 (hardcoded for this demo)
342      received_message = getEnvelopedDataStream(data, user1_pk, 0);
343      System.out.print("\nDecrypted content: ");
344      System.out.println(new String(received_message));
345      
346      System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n");
347      data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128);
348      // transmit data
349   
350      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
351      // user1 means index 0 (hardcoded for this demo)
352      System.out.println("Decrypt EncryptedContentInfo of EnvelopedData...");
353      received_message = getEncryptedContentInfoStream(data, user1_pk, 0);
354      System.out.print("\nDecrypted content: ");
355      System.out.println(new String(received_message));
356     
357  
358        } catch (Exception ex) {
359          ex.printStackTrace();
360          throw new RuntimeException(ex.toString());
361        }
362  }
363
364  /**
365   * The main method.
366   *
367   * @throws Exception
368   *            if some error occurs
369   */
370  public static void main(String argv[]) throws Exception {
371
372        demo.DemoUtil.initDemos();
373
374    (new ArcFourEnvelopedDataDemo()).start();
375    System.out.println("\nReady!");
376    System.in.read();
377  }
378}