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