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/FileEncryptionDemo.java 13    12.02.25 17:58 Dbratko $
059    // $Revision: 13 $
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.ContentInfoStream;
068    import iaik.cms.EnvelopedDataStream;
069    import iaik.cms.PasswordRecipientInfo;
070    import iaik.cms.RecipientInfo;
071    import iaik.security.random.SecRandom;
072    import iaik.security.spec.PBEKeyAndParameterSpec;
073    import iaik.utils.Util;
074    
075    import java.io.BufferedInputStream;
076    import java.io.BufferedOutputStream;
077    import java.io.FileInputStream;
078    import java.io.FileOutputStream;
079    import java.io.IOException;
080    import java.io.InputStream;
081    import java.io.OutputStream;
082    import java.security.SecureRandom;
083    
084    import javax.crypto.SecretKey;
085    
086    import demo.DemoUtil;
087    
088    /**
089     * This class shows how to use the CMS {@link iaik.cms.PasswordRecipientInfo
090     * PasswordRecipientInfo} type for password based encrypting the contents of 
091     * a file (and later decrypting it again) with the CMS {@link iaik.cms.EnvelopedDataStream
092     * EnvelopedDataStream} EnvelopedData} content type.
093     * <p>
094     * The contents is encrypted using the AES cipher. For deriving the key
095     * encryption key from the password, the PKCS#5 PBKDF2 key derivation function
096     * is used. The content encryption key is decrypted with AES, too. 
097     * <p>
098     * You can modify this demo to use any other supported algorithm(s). Or
099     * you can use it with PBKDF2/AES for encrypting a file simply
100     * by calling:
101     * <pre>
102     * // the file to be encrypted
103     * String dataFile = ...;
104     * // the file to which to write the encrypted data
105     * String encryptedFile = ...;
106     * // password 
107     * char[] password = ...;
108     * // encrypt file
109     * FileEncryption fe = new FileEncryption();
110     * fe.encrypt(dataFile, encryptedFile, password);
111     * </pre>
112     * 
113     * Or decrypting a file:
114     * 
115     * <pre>
116     * // the encrypted file
117     * String encryptedFile = ...;
118     * // the file to which to write the decrypted data
119     * String decryptedFile = ...;
120     * // password 
121     * char[] password = ...;
122     * // decrypt file
123     * FileEncryption fe = new FileEncryption();
124     * fe.decrypt(encryptedFile, decryptedFile, password);
125     * </pre>
126     * 
127     * @see iaik.cms.EnvelopedDataStream
128     * @see iaik.cms.EnvelopedData
129     * @see iaik.cms.EnvelopedDataOutputStream
130     * @see iaik.cms.PasswordRecipientInfo
131     */
132    public class FileEncryptionDemo {
133    
134      /**
135       * Default constructor.
136       */
137      public FileEncryptionDemo() {
138        System.out.println();
139        System.out.println("**********************************************************************************");
140        System.out.println("*                   File encryption/decryption demo                              *");
141        System.out.println("*    (shows how to use the PasswordRecipientInfo type for encrypting a file)     *");
142        System.out.println("**********************************************************************************");
143        System.out.println();
144        
145      }
146    
147     
148      /**
149       * Uses the given password to encrypt the contents from <code>inFile</code>
150       * and write the encryption result to <code>outFile</code>.
151       * The contents is encrypted using the AES cipher. For dereiving the key
152       * encryption key from the password, the PKCS#5 PBKDF2 key derivation function
153       * is used. The content encryption key is decrypted with AES, too. 
154       *
155       * @param inFile the file from which to read the encrypted (enveloped) data
156       * @param outFile the file to which to write the decrypted data
157       * @param password the password to be used for decryption
158       *
159       * @throws Exception if an error occurs
160       */
161      public void encrypt(String inFile,
162                          String outFile,
163                          char[] password) 
164        throws Exception {
165    
166        // stream for reading the data from the file to be encrypted
167        InputStream is = null;
168        // stream for writing the encrypted data to a file
169        OutputStream os = null;
170        
171        // key derivation function (PBKDF2)
172        AlgorithmID keyDerivationAlg = (AlgorithmID)AlgorithmID.pbkdf2.clone();
173        // key encryption algorithm (PWRI-KEK)
174        AlgorithmID keyEncryptionAlg = (AlgorithmID)CMSAlgorithmID.pwri_kek.clone();
175        // for PWRI-KEK set the kek encryption algorithm parameter (AES)
176        AlgorithmID kekEncryptionAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone();
177        keyEncryptionAlg.setParameter(kekEncryptionAlg.toASN1Object());
178        // content encryption algorithm
179        AlgorithmID cekAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone();
180        
181        // parameters for key derivation
182        int kekLen = 32;  // we use AES as kek algorithm
183        int iterationCount = 10000; 
184        byte[] salt = new byte[32];
185        SecureRandom random = SecRandom.getDefault();
186        random.nextBytes(salt);
187        PBEKeyAndParameterSpec keyDerivationParamSpec =
188          new PBEKeyAndParameterSpec(UTF8String.getUTF8EncodingFromCharArray(password),
189                                     salt,
190                                     iterationCount,
191                                     kekLen); 
192        
193        try {
194          
195          is = new BufferedInputStream(new FileInputStream(inFile));
196          
197          // create EnvelopedData for encrypting the data with the content encryption algorithm
198          EnvelopedDataStream envelopedData = new EnvelopedDataStream(is, cekAlg);
199          
200          // create the PasswordRecipientInfo
201          PasswordRecipientInfo pri = new PasswordRecipientInfo(password,
202                                                                keyDerivationAlg,
203                                                                keyDerivationParamSpec,
204                                                                keyEncryptionAlg,                               
205                                                                null);
206          // set the RecipientInfo
207          envelopedData.setRecipientInfos(new RecipientInfo[] { pri });
208          envelopedData.setBlockSize(2048);
209          // wrap into ContentInfo
210          ContentInfoStream cis = new ContentInfoStream(envelopedData);
211          
212          // encrypt to file
213          os = new BufferedOutputStream(new FileOutputStream(outFile));
214          cis.writeTo(os);
215           
216        } finally {
217          if (is != null) {
218            try {
219              is.close();
220            } catch (IOException ex) {
221              // ignore
222            }
223          }
224          if (os != null) {
225            try {
226              os.close();
227            } catch (IOException ex) {
228              // ignore
229            }
230          }
231        }
232       
233      }
234    
235      /**
236       * Uses the given password to decrypt the contents from <code>inFile</code>
237       * and write the it to <code>outFile</code>. 
238       *
239       * @param inFile the file from which to read the encrypted (enveloped) data
240       * @param outFile the file to which to write the decrypted data
241       * @param password the password to be used for decryption
242       *
243       * @throws Exception if an error occurs
244       */
245      public void decrypt(String inFile, String outFile, char[] password)
246        throws Exception {
247    
248        // stream for reading the encrypted data from a file
249        InputStream is = null;
250        // stream for writing the decrypted data to a file
251        OutputStream os = null;
252        
253        try {
254          
255          is = new BufferedInputStream(new FileInputStream(inFile));
256          
257          // create EnvelopedData 
258          EnvelopedDataStream envelopedData = new EnvelopedDataStream(is);
259          
260          // get PasswordRecipientInfo and decrypt the cek
261          PasswordRecipientInfo recipient = (PasswordRecipientInfo)envelopedData.getRecipientInfos()[0];
262          SecretKey cek = recipient.decryptKey(password, "AES-256"); 
263          String alg = cek.getAlgorithm();
264          // setup cipher for content decryption
265          envelopedData.setupCipher(cek);
266          InputStream decrypted = envelopedData.getInputStream();
267          // decrypt data to file
268          os = new BufferedOutputStream(new FileOutputStream(outFile));
269          Util.copyStream(decrypted, os, null);
270          
271        } finally {
272          if (is != null) {
273            try {
274              is.close();
275            } catch (IOException ex) {
276              // ignore
277            }
278          }
279          if (os != null) {
280            try {
281              os.close();
282            } catch (IOException ex) {
283              // ignore
284            }
285          }
286        }
287        
288      }
289    
290    
291      /**
292       * Starts the demo.
293       */
294      public void start() {
295        // the file to be encrypted
296        String dataFile = "test.html";
297        // the file to which to write the encrypted data
298        String encryptedFile = "encrypted.dat";
299        // the file to which to write the decrypted data
300        String decryptedFile = "decrypted.html";
301        
302        // password (in practice use a more secure one!)
303        char[] password = { 't', 'o', 'p', 'S', 'e', 'c', 'r', 'e', 't' };
304        
305        try {
306          
307          // encrypt file
308          System.out.println("Encrypt data from file " + dataFile + " to file " + encryptedFile);
309          encrypt(dataFile, encryptedFile, password);
310    
311          // decrypt file
312          System.out.println("Decrypt data from file " + encryptedFile + " to file " + decryptedFile);
313          decrypt(encryptedFile, decryptedFile, password);
314          
315        } catch (Exception ex) {  
316          ex.printStackTrace();
317          throw new RuntimeException(ex.toString());  
318        } finally {
319          for (int i = 0; i < password.length; i++) { 
320            password[i] = '\u0000';
321          }  
322        }
323    
324      }
325    
326    
327      /**
328       * Main method.
329       *
330       * @throws Exception
331       *            if some error occurs 
332       */
333      public static void main(String argv[]) throws Exception {
334    
335            DemoUtil.initDemos();
336    
337        (new FileEncryptionDemo()).start();
338        System.out.println("\nReady!");
339        DemoUtil.waitKey();
340      }
341    }