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