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/PasswordRecipientInfoDemo.java 31    12.02.25 17:58 Dbratko $
029// $Revision: 31 $
030//
031
032package demo.cms.envelopedData;
033
034import iaik.asn1.UTF8String;
035import iaik.asn1.structures.AlgorithmID;
036import iaik.cms.CMSAlgorithmID;
037import iaik.cms.CMSException;
038import iaik.cms.ContentInfo;
039import iaik.cms.ContentInfoStream;
040import iaik.cms.EncryptedContentInfo;
041import iaik.cms.EncryptedContentInfoStream;
042import iaik.cms.EnvelopedData;
043import iaik.cms.EnvelopedDataStream;
044import iaik.cms.PasswordRecipientInfo;
045import iaik.cms.RecipientInfo;
046import iaik.security.random.SecRandom;
047import iaik.security.spec.PBEKeyAndParameterSpec;
048import iaik.utils.Util;
049
050import java.io.ByteArrayInputStream;
051import java.io.ByteArrayOutputStream;
052import java.io.IOException;
053import java.io.InputStream;
054import java.security.AlgorithmParameters;
055import java.security.NoSuchAlgorithmException;
056import java.security.SecureRandom;
057import java.security.spec.AlgorithmParameterSpec;
058
059import javax.crypto.SecretKey;
060
061import demo.DemoUtil;
062
063/**
064 * This class shows the usage of the CMS RecipientInfo type {@link iaik.cms.PasswordRecipientInfo
065 * PasswordRecipientInfo} as specified by <a href = "http://www.ietf.org/rfc/rfc5652.txt" target="_blank"> RFC 5652</a>.
066 */
067public class PasswordRecipientInfoDemo {
068
069  
070  // secure random number generator
071  SecureRandom random;
072
073  /**
074   * Default constructor.
075   */
076  public PasswordRecipientInfoDemo() {
077    System.out.println();
078    System.out.println("**********************************************************************************");
079    System.out.println("*                   PasswordRecipientInfoDemo                                    *");
080    System.out.println("*    (shows the usage of the CMS PasswordRecipientInfo type implementation)      *");
081    System.out.println("**********************************************************************************");
082    System.out.println();
083    
084    random = SecRandom.getDefault();
085  }
086
087 
088  /**
089   * Creates a CMS <code>EnvelopedData</code> with a PasswordRecipientInfo
090   * and wraps it into a ContentInfo (stream implementation).
091   *
092   * @param message the message to be enveloped, as byte representation
093   * @param password the password from which to derive the key encryption key (kek)
094   * @param keyDerivationAlg the key derivation function to be used for deriving the kek
095   * @param keyDerivatoinParamSpec any parameters required by the key derivation function
096   * @param keyEncrAlg the ID of the key-encryption (key-wrap) algorithm to be used
097   *                   for encrypting the content-encryption key
098   * @param keyEncrParams any algorithm parameters to be used for intializing the 
099   *                          key wrap cipher
100   * @return the encoded ContentInfo containing the EnvelopedData object just created
101   *
102   * @throws CMSException if the <code>EnvelopedData</code> object cannot
103   *                          be created
104   * @throws IOException if an I/O error occurs
105   */
106  public byte[] createEnvelopedDataStream(byte[] message,
107                                          char[] password,
108                                          AlgorithmID keyDerivationAlg,
109                                          AlgorithmParameterSpec keyDerivatoinParamSpec,
110                                          AlgorithmID keyEncrAlg,                               
111                                          AlgorithmParameters keyEncrParams) 
112    throws CMSException, IOException {
113
114    EnvelopedDataStream envelopedData;
115
116    // we are testing the stream interface
117    ByteArrayInputStream is = new ByteArrayInputStream(message);
118    // create a new EnvelopedData object encrypted with AES
119    try {
120      envelopedData = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
121    } catch (NoSuchAlgorithmException ex) {
122      throw new CMSException("Cannot init EnvelopedDataStream: " + ex.toString());
123    }
124
125    // create the PasswordRecipientInfo
126    PasswordRecipientInfo pri;
127    try {
128      pri = new PasswordRecipientInfo(password,
129                                      keyDerivationAlg,
130                                      keyDerivatoinParamSpec,
131                                      keyEncrAlg,                               
132                                      keyEncrParams);
133    } catch (Exception ex) {
134      throw new CMSException("Cannot create PasswordRecipientInfo: " + ex.toString());   
135    }    
136        
137    
138    // specify the recipients of the encrypted message
139    RecipientInfo[] recipients = { pri };
140    envelopedData.setRecipientInfos(recipients);
141    // return the EnvelopedDate as DER encoded byte array with block size 2048
142    ByteArrayOutputStream os = new ByteArrayOutputStream();
143    envelopedData.setBlockSize(2048);
144    ContentInfoStream cis = new ContentInfoStream(envelopedData);
145    cis.writeTo(os);
146    return os.toByteArray();
147  }
148
149  /**
150   * PBE based decrypts the encrypted content of the given EnvelopedData object 
151   * and returns the decrypted (= original) message (stream implementation).
152   *
153   * @param encoding the encoded ContentInfo containing an EnvelopedData object
154   * @param password the password from which to derive the key-encryption key (kek)
155   *                 to be used for decrypting the content-encryption key (cek)
156   * @param cekAlgName the name of the cek (content encryption key) algorithm
157   *
158   * @return the recovered message, as byte array
159   * @throws CMSException if the message cannot be recovered
160   * @throws IOException if an I/O error occurs
161   */
162  public byte[] getEnvelopedDataStream(byte[] encoding, char[] password, String cekAlgName) throws CMSException, IOException {
163
164    // create the EnvelopedData object from a DER encoded byte array
165    // we are testing the stream interface
166    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
167    EnvelopedDataStream envelopedData = new EnvelopedDataStream(is);
168
169    System.out.println("Information about the encrypted data:");
170    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)envelopedData.getEncryptedContentInfo();
171    System.out.println("Content type: "+eci.getContentType().getName());
172    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
173
174    System.out.println("\nInformation about the RecipientInfo :");
175    PasswordRecipientInfo recipient = (PasswordRecipientInfo)envelopedData.getRecipientInfos()[0];
176    System.out.println(recipient);
177
178    // decrypt the message
179    try {
180      SecretKey cek = recipient.decryptKey(password, cekAlgName); 
181      envelopedData.setupCipher(cek);
182      InputStream decrypted = envelopedData.getInputStream();
183      ByteArrayOutputStream os = new ByteArrayOutputStream();
184      Util.copyStream(decrypted, os, null);
185
186      return os.toByteArray();
187
188    } catch (Exception ex) {
189      throw new CMSException("Cannot decrypt message. " + ex.toString());
190    }
191  }
192
193
194  /**
195   * Creates a CMS <code>EnvelopedData</code> with a PasswordRecipientInfo
196   * and wraps it into a ContentInfo.
197   *
198   * @param message the message to be enveloped, as byte representation
199   * @param password the password from which to derive the key encryption key (kek)
200   * @param keyDerivationAlg the key derivation function to be used for deriving the kek
201   * @param keyDerivatoinParamSpec any parameters required by the key derivation function
202   * @param keyEncrAlg the ID of the key-encryption (key-wrap) algorithm to be used
203   *                   for encrypting the content-encryption key
204   * @param keyEncrParams any algorithm parameters to be used for intializing the 
205   *                          key wrap cipher
206   * @return the encoded ContentInfo containing the EnvelopedData object just created
207   *
208   * @throws CMSException if the <code>EnvelopedData</code> object cannot
209   *                          be created
210   */
211  public byte[] createEnvelopedData(byte[] message, 
212                                    char[] password,
213                                    AlgorithmID keyDerivationAlg,
214                                    AlgorithmParameterSpec keyDerivatoinParamSpec,
215                                    AlgorithmID keyEncrAlg,                               
216                                    AlgorithmParameters keyEncrParams) 
217    throws CMSException {
218
219    EnvelopedData envelopedData;
220
221    // create a new EnvelopedData object encrypted with AES
222    try {
223      envelopedData = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
224    } catch (NoSuchAlgorithmException ex) {
225      throw new CMSException(ex.toString());
226    }
227
228    // create the PasswordRecipientInfo
229    PasswordRecipientInfo pri;
230    try {
231      pri = new PasswordRecipientInfo(password,
232                                      keyDerivationAlg,
233                                      keyDerivatoinParamSpec,
234                                      keyEncrAlg,                               
235                                      keyEncrParams);
236    } catch (Exception ex) {
237      throw new CMSException("Cannot create PasswordRecipientInfo: " + ex.toString());   
238    }    
239        
240    
241    // specify the recipients of the encrypted message
242    RecipientInfo[] recipients = { pri };
243    envelopedData.setRecipientInfos(recipients);
244    
245    // wrap into contentInfo
246    ContentInfo ci = new ContentInfo(envelopedData);
247    // return the EnvelopedDate as DER encoded byte array
248    return ci.toByteArray();
249  }
250
251  /**
252   * PBE based decrypts the encrypted content of the given EnvelopedData object 
253   * and returns the decrypted (= original) message.
254   *
255   * @param encoding the encoded ContentInfo containing an EnvelopedData object
256   * @param password the password from which to derive the key-encryption key (kek)
257   *                 to be used for decrypting the content-encryption key (cek)
258   * @param cekAlgName the name of the cek (content encryption key) algorithm
259   *
260   * @return the recovered message, as byte array
261   *
262   * @throws CMSException if the message cannot be recovered
263   * @throws IOException if an I/O error occurs
264   */
265  public byte[] getEnvelopedData(byte[] encoding, char[] password, String cekAlgName) throws CMSException, IOException {
266    
267    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
268
269    EnvelopedData envelopedData = new EnvelopedData(is);
270
271    System.out.println("Information about the encrypted data:");
272    EncryptedContentInfo eci = (EncryptedContentInfo)envelopedData.getEncryptedContentInfo();
273    System.out.println("Content type: "+eci.getContentType().getName());
274    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
275
276    System.out.println("\nInformation about the RecipientInfo :");
277    PasswordRecipientInfo recipient = (PasswordRecipientInfo)envelopedData.getRecipientInfos()[0];
278    System.out.println(recipient);
279
280    // decrypt the message
281    try {
282      SecretKey cek = recipient.decryptKey(password, cekAlgName); 
283      envelopedData.setupCipher(cek);
284      return envelopedData.getContent();
285
286    } catch (Exception ex) {
287      throw new CMSException("Cannot decrypt message: " + ex.toString());
288    }
289  }
290  
291
292
293  /**
294   * Starts the demo.
295   */
296  public void start() {
297     // the test message
298    String m = "This is the test message.";
299    System.out.println("Test message: \""+m+"\"");
300    System.out.println();
301    byte[] message = m.getBytes();
302    
303    // the password
304    char[] password = "topSecret".toCharArray();
305
306    try {
307      byte[] encodedEnvelopedData;
308      byte[] receivedMessage = null;
309      
310      int kekLen = 32;  // we use AES with a 256 bit key as kek algorithm
311      int iterationCount = 10000; 
312      byte[] salt = new byte[32];
313      random.nextBytes(salt);
314      PBEKeyAndParameterSpec keyDerivationParamSpec =
315        new PBEKeyAndParameterSpec(UTF8String.getUTF8EncodingFromCharArray(password),
316                                   salt,
317                                   iterationCount,
318                                   kekLen); 
319
320      
321      System.out.println("Stream implementation demo");
322      System.out.println("===========================");
323
324      // the stream implementation
325      // test CMS EnvelopedDataStream
326      //
327      System.out.println("\nEnvelopedDataStream demo [create]:\n");
328      // key derivation function
329      AlgorithmID keyDerivationAlg = (AlgorithmID)AlgorithmID.pbkdf2.clone();
330      // key encryption algorithm
331      AlgorithmID keyEncryptionAlg = (AlgorithmID)CMSAlgorithmID.pwri_kek.clone();
332      // for PWRI-KEK set the kek encryption algorithm parameter
333      AlgorithmID kekEncryptionAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone();
334      keyEncryptionAlg.setParameter(kekEncryptionAlg.toASN1Object());
335      // the name of the content encryption algorithm
336      String cekAlgName = "AES256";
337      // we can use null as password since it is already set in keyDerivationParamSpec
338      encodedEnvelopedData = createEnvelopedDataStream(message,
339                                                       null,
340                                                       (AlgorithmID)keyDerivationAlg.clone(),
341                                                       keyDerivationParamSpec,
342                                                       keyEncryptionAlg,
343                                                       null);
344      // transmit data
345      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
346      // user1 means index 0 (hardcoded for this demo)
347      receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, password, cekAlgName);
348      System.out.print("\nDecrypted content: ");
349      System.out.println(new String(receivedMessage));
350
351      // the non-stream implementation
352      System.out.println("\nNon-stream implementation demo");
353      System.out.println("===============================");
354      
355      
356      //
357      // test CMS EnvelopedData
358      //
359      System.out.println("\nEnvelopedData demo [create]:\n");
360      // key derivation function
361      keyDerivationAlg = (AlgorithmID)AlgorithmID.pbkdf2.clone();
362      // key encryption algorithm
363      keyEncryptionAlg = (AlgorithmID)CMSAlgorithmID.pwri_kek.clone();
364      // for PWRI-KEK set the kek encryption algorithm parameter
365      kekEncryptionAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone();
366      keyEncryptionAlg.setParameter(kekEncryptionAlg.toASN1Object());
367      // the name of the content encryption algorithm
368      cekAlgName = "AES256";
369      // we can use null as password since it is already set in keyDerivationParamSpec
370      encodedEnvelopedData = createEnvelopedData(message,
371                                                 null,
372                                                 keyDerivationAlg,
373                                                 keyDerivationParamSpec,
374                                                 keyEncryptionAlg,
375                                                 null);
376      // transmit data
377      System.out.println("\nEnvelopedData demo [parse]:\n");
378      // user1 means index 0 (hardcoded for this demo)
379      receivedMessage = getEnvelopedData(encodedEnvelopedData, password, cekAlgName);
380      System.out.print("\nDecrypted content: ");
381      System.out.println(new String(receivedMessage));
382
383
384
385      System.out.println("Ready!");
386
387
388
389        } catch (Exception ex) {
390          ex.printStackTrace();
391          throw new RuntimeException(ex.toString());
392        }
393  }
394
395
396  /**
397   * Main method.
398   *
399   * @throws Exception
400   *            if some error occurs 
401   */
402  public static void main(String argv[]) throws Exception {
403
404        DemoUtil.initDemos();
405
406    (new PasswordRecipientInfoDemo()).start();
407
408    DemoUtil.waitKey();
409  }
410}