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 }