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 }