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/ecc/EdDHEnvelopedDataDemo.java 6 12.02.25 17:58 Dbratko $
059 // $Revision: 6 $
060 //
061
062
063 package demo.cms.ecc;
064
065 import iaik.asn1.ObjectID;
066 import iaik.asn1.structures.AlgorithmID;
067 import iaik.cms.CMSAlgorithmID;
068 import iaik.cms.CMSException;
069 import iaik.cms.CertificateIdentifier;
070 import iaik.cms.ContentInfo;
071 import iaik.cms.ContentInfoOutputStream;
072 import iaik.cms.ContentInfoStream;
073 import iaik.cms.EncryptedContentInfo;
074 import iaik.cms.EncryptedContentInfoStream;
075 import iaik.cms.EnvelopedData;
076 import iaik.cms.EnvelopedDataOutputStream;
077 import iaik.cms.EnvelopedDataStream;
078 import iaik.cms.IssuerAndSerialNumber;
079 import iaik.cms.KeyAgreeRecipientInfo;
080 import iaik.cms.KeyIdentifier;
081 import iaik.cms.RecipientInfo;
082 import iaik.cms.RecipientKeyIdentifier;
083 import iaik.security.random.SecRandom;
084 import iaik.utils.Util;
085 import iaik.x509.X509Certificate;
086
087 import java.io.ByteArrayInputStream;
088 import java.io.ByteArrayOutputStream;
089 import java.io.IOException;
090 import java.io.InputStream;
091 import java.security.InvalidKeyException;
092 import java.security.Key;
093 import java.security.NoSuchAlgorithmException;
094 import java.security.PrivateKey;
095 import java.security.SecureRandom;
096
097 import javax.crypto.SecretKey;
098
099 import demo.DemoUtil;
100 import demo.cms.ecc.keystore.CMSEccKeyStore;
101
102 /**
103 * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataStream},
104 * {@link iaik.cms.EnvelopedData} and {@link iaik.cms.EnvelopedDataOutputStream}
105 * for encrypting data using the CMS type EnvelopedData by using the
106 * Ephemeral-Static ECDH Elliptic Curve Diffie-Hellman (ECDH) key agreement
107 * algorithm with curve25519 and curve448 in the Cryptographic Message Syntax (CMS)
108 * according to <a href = "http://www.ietf.org/rfc/rfc8418.txt" target="_blank">RFC 8418</a>.
109 * <p>
110 * Any keys/certificates required for this demo are read from a keystore
111 * file "cmsecc.keystore" located in your current working directory. If
112 * the keystore file does not exist you can create it by running the
113 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
114 * program.
115 * <p>
116 * Additionally to <code>iaik_cms.jar</code> you also must have
117 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
118 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
119 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
120 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
121 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
122 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
123 * in your classpath.
124 *
125 * @see iaik.cms.EnvelopedDataStream
126 * @see iaik.cms.EnvelopedData
127 * @see iaik.cms.RecipientInfo
128 * @see iaik.cms.KeyAgreeRecipientInfo
129 * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore
130 */
131 public class EdDHEnvelopedDataDemo {
132
133 // certificate of x25519 user
134 X509Certificate x25519User;
135 // private key of x25519 user
136 PrivateKey x25519User_pk;
137 // certificate of x448 user
138 X509Certificate x448User;
139 // private key of x448 user
140 PrivateKey x448User_pk;
141
142 // secure random number generator
143 SecureRandom random;
144
145 /**
146 * Setup the demo certificate chains.
147 *
148 * Keys and certificates are retrieved from the demo keyStore file
149 * "cmsecc.keystore" located in your current working directory. If
150 * the keystore file does not exist you can create it by running the
151 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
152 * program.
153 *
154 * @throws IOException if keys/certificates cannot be read from the keystore
155 */
156 public EdDHEnvelopedDataDemo() throws IOException {
157
158 System.out.println();
159 System.out.println("**********************************************************************************");
160 System.out.println("* EdDHEnvelopedDataDemo demo *");
161 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for ECDH) *");
162 System.out.println("**********************************************************************************");
163 System.out.println();
164
165 // get keys and certificate from the demo keystore
166 x25519User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519)[0];
167 x25519User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519);
168 x448User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448)[0];
169 x448User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448);
170
171 random = SecRandom.getDefault();
172
173 }
174
175 /**
176 * Creates a CMS <code>EnvelopedDataStream</code> message.
177 *
178 * @param message the message to be enveloped, as byte representation
179 * @param keyEA the key encryption (key agreement) algorithm used for creating
180 * a shared key encryption key for encrypting the secret content
181 * encryption key with it
182 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
183 * the content encryption key
184 * @param kekLength the length of the key encryption key to be created for
185 * encrypting the content encryption key with it
186 *
187 * @return the DER encoding of the <code>EnvelopedData</code> object just created
188 * @throws CMSException if the <code>EnvelopedData</code> object cannot
189 * be created
190 * @throws IOException if an I/O error occurs
191 */
192 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException, IOException {
193
194 EnvelopedDataStream enveloped_data;
195
196 // we are testing the stream interface
197 ByteArrayInputStream is = new ByteArrayInputStream(message);
198 // create a new EnvelopedData object encrypted with AES-256 CBC
199 try {
200 enveloped_data = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
201 } catch (NoSuchAlgorithmException ex) {
202 throw new CMSException("No implementation for AES256-CBC: " + ex.toString());
203 }
204
205
206 // create the recipient infos
207 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
208 // specify the recipients of the encrypted message
209 enveloped_data.setRecipientInfos(recipients);
210
211 // return the EnvelopedDate as BER encoded byte array with block size 4
212 // (just for testing; in real application we will use a proper blocksize,
213 // e.g. 2048, 4096,..)
214 enveloped_data.setBlockSize(4);
215 ByteArrayOutputStream os = new ByteArrayOutputStream();
216 ContentInfoStream cis = new ContentInfoStream(enveloped_data);
217 cis.writeTo(os);
218 return os.toByteArray();
219 }
220
221
222 /**
223 * Creates a CMS <code>EnvelopedData</code> message using the
224 * {@link iaik.cms.EnvelopedDataOutputStream EnvelopedDataOutputStream}
225 * class.
226 *
227 * @param message the message to be enveloped, as byte representation
228 * @param keyEA the key encryption (key agreement) algorithm used for creating
229 * a shared key encryption key for encrypting the secret content
230 * encryption key with it
231 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
232 * the content encryption key
233 * @param kekLength the length of the key encryption key to be created for
234 * encrypting the content encryption key with it
235 *
236 * @return the DER encoding of the <code>EnvelopedData</code> object just created
237 * @throws CMSException if the <code>EnvelopedData</code> object cannot
238 * be created
239 * @throws IOException if an I/O error occurs
240 */
241 public byte[] createEnvelopedDataOutputStream(byte[] message, AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException, IOException {
242
243 EnvelopedDataStream enveloped_data;
244
245 // a stream from which to read the data to be encrypted
246 ByteArrayInputStream is = new ByteArrayInputStream(message);
247
248 // the stream to which to write the EnvelopedData
249 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
250 EnvelopedDataOutputStream envelopedData;
251
252 // wrap EnvelopedData into a ContentInfo
253 ContentInfoOutputStream contentInfoStream =
254 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
255 // create a new EnvelopedData object encrypted with AES-256 CBC
256 try {
257 envelopedData = new EnvelopedDataOutputStream(contentInfoStream, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
258 } catch (NoSuchAlgorithmException ex) {
259 throw new CMSException("No implementation for AES256-CBC: " + ex.toString());
260 }
261
262
263 // create the recipient infos
264 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
265 // specify the recipients of the encrypted message
266 envelopedData.setRecipientInfos(recipients);
267
268 int blockSize = 4; // in real world we would use a block size like 2048
269 // write in the data to be encrypted
270 byte[] buffer = new byte[blockSize];
271 int bytesRead;
272 while ((bytesRead = is.read(buffer)) != -1) {
273 envelopedData.write(buffer, 0, bytesRead);
274 }
275
276 // closing the stream finishes encryption and closes the underlying stream
277 envelopedData.close();
278 return resultStream.toByteArray();
279 }
280
281 /**
282 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
283 * the recipient identified by its index into the recipientInfos field.
284 *
285 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
286 * @param key the key to decrypt the message
287 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
288 * to which the specified key belongs
289 *
290 * @return the recovered message, as byte array
291 * @throws CMSException if the message cannot be recovered
292 * @throws IOException if a stream read/write error occurs
293 */
294 public byte[] getEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
295 throws CMSException, IOException {
296
297 // create the EnvelopedData object from a DER encoded byte array
298 // we are testing the stream interface
299 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
300 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
301
302 System.out.println("Information about the encrypted data:");
303 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
304 System.out.println("Content type: "+eci.getContentType().getName());
305 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
306
307 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
308 RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
309
310 // for demonstration purposes we only look one time for all recipients included:
311 if (recipientInfoIndex == 0) {
312 int k = 0;
313 for (int i=0; i<recipients.length; i++) {
314 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
315 for (int j = 0; j < recipientIDs.length; j++) {
316 System.out.println("Recipient "+(++k)+":");
317 System.out.println(recipientIDs[j]);
318 }
319 }
320 }
321 // decrypt the message for the first recipient
322 try {
323 enveloped_data.setupCipher(key, recipientInfoIndex);
324 InputStream decrypted = enveloped_data.getInputStream();
325 ByteArrayOutputStream os = new ByteArrayOutputStream();
326 Util.copyStream(decrypted, os, null);
327
328 return os.toByteArray();
329
330 } catch (InvalidKeyException ex) {
331 throw new CMSException("Private key error: "+ex.getMessage());
332 } catch (NoSuchAlgorithmException ex) {
333 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
334 }
335 }
336
337 /**
338 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
339 * the recipient identified by recipient identifier.
340 *
341 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array
342 * @param key the key to decrypt the message
343 * @param recipientID the recipient identifier uniquely identifying the key of the
344 * recipient
345 *
346 * @return the recovered message, as byte array
347 * @throws CMSException if the message cannot be recovered
348 * @throws IOException if a stream read/write error occurs
349 */
350 public byte[] getEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
351 throws CMSException, IOException {
352
353 // create the EnvelopedData object from a DER encoded byte array
354 // we are testing the stream interface
355 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
356 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
357
358 System.out.println("Information about the encrypted data:");
359 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
360 System.out.println("Content type: "+eci.getContentType().getName());
361 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
362
363 // get the right RecipientInfo
364 System.out.println("\nSearch for RecipientInfo:");
365 RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
366 if (recipient != null) {
367 System.out.println("RecipientInfo: " + recipient);
368 } else {
369 throw new CMSException("No recipient with ID: " + recipientID);
370 }
371 // decrypt the content encryption key and the content
372 try {
373 System.out.println("Decrypt encrypted content encryption key...");
374 SecretKey cek = recipient.decryptKey(key, recipientID);
375 System.out.println("Decrypt content with decrypted content encryption key...");
376 enveloped_data.setupCipher(cek);
377 InputStream decrypted = enveloped_data.getInputStream();
378 ByteArrayOutputStream os = new ByteArrayOutputStream();
379 Util.copyStream(decrypted, os, null);
380
381 return os.toByteArray();
382
383 } catch (InvalidKeyException ex) {
384 throw new CMSException("Private key error: "+ex.getMessage());
385 } catch (NoSuchAlgorithmException ex) {
386 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
387 }
388 }
389
390 // non stream
391
392 /**
393 * Creates a CMS <code>EnvelopedData</code> message.
394 *
395 * @param message the message to be enveloped, as byte representation
396 * @param keyEA the key encryption (key agreement) algorithm used for creating
397 * a shared key encryption key for encrypting the secret content
398 * encryption key with it
399 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
400 * the content encryption key
401 * @param kekLength the length of the key encryption key to be created for
402 * encrypting the content encryption key with it
403 *
404 * @return the encoded <code>EnvelopedData</code>, as byte array
405 *
406 * @throws CMSException if the <code>EnvelopedData</code> object cannot
407 * be created
408 */
409 public byte[] createEnvelopedData(byte[] message, AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException {
410
411 EnvelopedData enveloped_data;
412
413 // create a new EnvelopedData object encrypted with AES-256 CBC
414 try {
415 enveloped_data = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
416 } catch (NoSuchAlgorithmException ex) {
417 throw new CMSException("No implementation for AES256-CBC: " + ex.toString());
418 }
419
420 // set the RecipientInfos
421 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
422 enveloped_data.setRecipientInfos(recipients);
423
424 // return encoded EnvelopedData
425 // wrap into contentInfo
426 ContentInfo ci = new ContentInfo(enveloped_data);
427 return ci.getEncoded();
428 }
429
430
431 /**
432 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
433 * the recipient identified by its index into the recipientInfos field.
434 *
435 * @param enc the encoded <code>EnvelopedData</code>
436 * @param key the key to decrypt the message
437 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
438 * to which the specified key belongs
439 *
440 * @return the recovered message, as byte array
441 *
442 * @throws CMSException if the message cannot be recovered
443 * @throws IOException if an I/O error occurs
444 */
445 public byte[] getEnvelopedData(byte[] enc, Key key, int recipientInfoIndex)
446 throws CMSException, IOException {
447 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
448 EnvelopedData enveloped_data = new EnvelopedData(bais);
449
450 System.out.println("Information about the encrypted data:");
451 EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
452 System.out.println("Content type: "+eci.getContentType().getName());
453 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
454
455 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
456 RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
457
458 // for demonstration purposes we only look one time for all recipients included:
459 if (recipientInfoIndex == 0) {
460 int k = 0;
461 for (int i=0; i<recipients.length; i++) {
462 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
463 for (int j = 0; j < recipientIDs.length; j++) {
464 System.out.println("Recipient "+(++k)+":");
465 System.out.println(recipientIDs[j]);
466 }
467 }
468 }
469
470 // decrypt the message
471 try {
472 enveloped_data.setupCipher(key, recipientInfoIndex);
473 return enveloped_data.getContent();
474
475 } catch (InvalidKeyException ex) {
476 throw new CMSException("Private key error: "+ex.getMessage());
477 } catch (NoSuchAlgorithmException ex) {
478 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
479 }
480 }
481
482 /**
483 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
484 * the recipient identified by recipient identifier.
485 * <p>
486 * This way of decrypting the content may be used for any type of RecipientInfo
487 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The
488 * recipient in mind is identified by its recipient identifier.
489 *
490 * @param enc the encoded <code>AuthenticatedData</code>
491 * @param key the key to decrypt the message
492 * @param recipientID the recipient identifier uniquely identifying the key of the
493 * recipient
494 *
495 * @return the recovered message, as byte array
496 * @throws CMSException if the message cannot be recovered
497 * @throws IOException if an I/O error occurs
498 */
499 public byte[] getEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID)
500 throws CMSException, IOException {
501 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
502 EnvelopedData enveloped_data = new EnvelopedData(bais);
503
504 System.out.println("Information about the encrypted data:");
505 EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
506 System.out.println("Content type: "+eci.getContentType().getName());
507 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
508
509 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
510
511 // get the right RecipientInfo
512 System.out.println("\nSearch for RecipientInfo:");
513 RecipientInfo recipient = enveloped_data.getRecipientInfo(recipientID);
514 if (recipient != null) {
515 System.out.println("RecipientInfo: " + recipient);
516 } else {
517 throw new CMSException("No recipient with ID: " + recipientID);
518 }
519 // decrypt the content encryption key and the content
520 try {
521 System.out.println("Decrypt encrypted content encryption key...");
522 SecretKey cek = recipient.decryptKey(key, recipientID);
523 System.out.println("Decrypt content with decrypted content encryption key...");
524 enveloped_data.setupCipher(cek);
525 return enveloped_data.getContent();
526
527 } catch (InvalidKeyException ex) {
528 throw new CMSException("Private key error: "+ex.getMessage());
529 } catch (NoSuchAlgorithmException ex) {
530 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
531 }
532 }
533
534 /**
535 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
536 * the recipient identified by its recipient certificate.
537 *
538 * @param enc the encoded <code>EnvelopedData</code>
539 * @param key the key to decrypt the message
540 * @param recipientCert the certificate of the recipient
541 *
542 * @return the recovered message, as byte array
543 *
544 * @throws CMSException if the message cannot be recovered
545 */
546 public byte[] getEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert)
547 throws CMSException, IOException {
548 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
549 EnvelopedData enveloped_data = new EnvelopedData(bais);
550
551 System.out.println("Information about the encrypted data:");
552 EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
553 System.out.println("Content type: "+eci.getContentType().getName());
554 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
555
556 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
557
558 // decrypt the content encryption key and the content
559 try {
560 System.out.println("Decrypt the content...");
561 enveloped_data.setupCipher(key, recipientCert);
562 return enveloped_data.getContent();
563
564 } catch (InvalidKeyException ex) {
565 throw new CMSException("Private key error: "+ex.getMessage());
566 } catch (NoSuchAlgorithmException ex) {
567 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
568 }
569 }
570
571 /**
572 * Creates the RecipientInfos.
573 *
574 * @param keyEA the key encryption (key agreement) algorithm used for creating
575 * a shared key encryption key for encrypting the secret content
576 * encryption key with it
577 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
578 * the content encryption key
579 * @param kekLength the length of the key encryption key to be created for
580 * encrypting the content encryption key with it
581 *
582 * @return the RecipientInfos created, two KeyAgreeRecipientInfos
583 *
584 * @throws CMSException if an error occurs when creating the recipient infos
585 */
586 public RecipientInfo[] createRecipients(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException {
587
588 RecipientInfo[] recipients = new RecipientInfo[2];
589 try {
590 recipients[0] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
591 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(x25519User, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
592
593 recipients[1] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
594 ((KeyAgreeRecipientInfo)recipients[1]).addRecipient(x448User, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
595
596 } catch (Exception ex) {
597 throw new CMSException("Error adding recipients: " + ex.toString());
598 }
599 return recipients;
600 }
601
602 /**
603 * Parses an EnvelopedData and decrypts the content for all test recipients
604 * using the index into the recipientInfos field for identifying the recipient.
605 *
606 * @param stream whether to use EnvelopedDataStream or EnvelopedData
607 * @param encodedEnvelopedData the encoded EnvelopedData object
608 *
609 * @throws Exception if some error occurs during decoding/decryption
610 */
611 public void parseEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedEnvelopedData) throws Exception {
612 byte[] receivedMessage;
613 if (stream) {
614 // x25519User
615 System.out.println("\nDecrypt for x25519User:");
616 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, x25519User_pk, 0);
617 System.out.print("\nDecrypted content: ");
618 System.out.println(new String(receivedMessage));
619 // x448User
620 System.out.println("\nDecrypt for x448User:");
621 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, x448User_pk, 1);
622 System.out.print("\nDecrypted content: ");
623 System.out.println(new String(receivedMessage));
624 } else {
625 // x25519User
626 System.out.println("\nDecrypt for x25519User:");
627 receivedMessage = getEnvelopedData(encodedEnvelopedData, x25519User_pk, 0);
628 System.out.print("\nDecrypted content: ");
629 System.out.println(new String(receivedMessage));
630 // x448User
631 System.out.println("\nDecrypt for x448User:");
632 receivedMessage = getEnvelopedData(encodedEnvelopedData, x448User_pk, 1);
633 System.out.print("\nDecrypted content: ");
634 System.out.println(new String(receivedMessage));
635 }
636 }
637
638 /**
639 * Parses an EnvelopedData and decrypts the content for all test recipients
640 * using their recipient identifiers for identifying the recipient.
641 *
642 * @param stream whether to use EnvelopedDataStream or EnvelopedData
643 * @param encodedEnvelopedData the encoded EnvelopedData object
644 *
645 * @throws Exception if some error occurs during decoding/decryption
646 */
647 public void parseEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedEnvelopedData) throws Exception {
648 byte[] receivedMessage;
649 if (stream) {
650 // x25519User
651 System.out.println("\nDecrypt for x25519User:");
652 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
653 System.out.print("\nDecrypted content: ");
654 System.out.println(new String(receivedMessage));
655 // x448User
656 System.out.println("\nDecrypt for x448User:");
657 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
658 System.out.print("\nDecrypted content: ");
659 System.out.println(new String(receivedMessage));
660 } else {
661 // x25519User
662 System.out.println("\nDecrypt for x25519User:");
663 receivedMessage = getEnvelopedData(encodedEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
664 System.out.print("\nDecrypted content: ");
665 System.out.println(new String(receivedMessage));
666 // x448User
667 System.out.println("\nDecrypt for x448User:");
668 receivedMessage = getEnvelopedData(encodedEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
669 System.out.print("\nDecrypted content: ");
670 System.out.println(new String(receivedMessage));
671 }
672 }
673
674 /**
675 * Runs the demo.
676 */
677 public void start() {
678
679 AlgorithmID[] keyEAs = {
680 AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme,
681 AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme,
682 AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme,
683 AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme,
684 AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme,
685 };
686
687 AlgorithmID[] keyWrapAlgs = {
688 CMSAlgorithmID.cms_aes128_wrap,
689 CMSAlgorithmID.cms_aes192_wrap,
690 CMSAlgorithmID.cms_aes256_wrap,
691 };
692
693 for (int i = 0; i < keyEAs.length; i++) {
694 AlgorithmID keyEA = keyEAs[i];
695 for (int j = 0; j < keyWrapAlgs.length; j++) {
696 AlgorithmID keyWrapAlg = keyWrapAlgs[j];
697 int kekLength = 256;
698 if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes192_wrap)) {
699 kekLength = 192;
700 } else if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes128_wrap)) {
701 kekLength = 128;
702 }
703 System.out.println("EdDH demo for " + keyEA.getName() + " with " + keyWrapAlg.getName());
704 start(keyEA, keyWrapAlg, kekLength);
705 }
706 }
707 }
708
709 /**
710 * Runs the demo for the given key encryption (key agreement) algorithm,
711 * and key wrap algorithm.
712 *
713 * @param keyEA the key encryption (key agreement) algorithm used for creating
714 * a shared key encryption key for encrypting the secret content
715 * encryption key with it
716 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
717 * the content encryption key
718 * @param kekLength the length of the key encryption key to be created for
719 * encrypting the content encryption key with it
720 */
721 public void start(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) {
722 // the test message
723 String m = "This is the test message.";
724 System.out.println("Test message: \""+m+"\"");
725 System.out.println();
726 byte[] message = m.getBytes();
727
728 try {
729 byte[] encoding;
730 System.out.println("Stream implementation demos");
731 System.out.println("===========================");
732
733 // the stream implementation
734 //
735 // test CMS EnvelopedDataStream
736 //
737 System.out.println("\nCMS EnvelopedDataStream demo [create]:\n");
738 encoding = createEnvelopedDataStream(message, keyEA, keyWrapAlg, kekLength);
739 // transmit data
740 System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n");
741 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
742 parseEnvelopedDataWithRecipientInfoIndex(true, encoding);
743 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
744 parseEnvelopedDataWithRecipientIdentifier(true, encoding);
745
746 System.out.println("OutputStream implementation demos");
747 System.out.println("===========================");
748
749 // the stream implementation
750 //
751 // test CMS EnvelopedDataOutputStream
752 //
753 System.out.println("\nCMS EnvelopedDataOutputStream demo [create]:\n");
754 encoding = createEnvelopedDataOutputStream(message, keyEA, keyWrapAlg, kekLength);
755 // transmit data
756 System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n");
757 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
758 parseEnvelopedDataWithRecipientInfoIndex(true, encoding);
759 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
760 parseEnvelopedDataWithRecipientIdentifier(true, encoding);
761
762 // the non-stream implementation
763 System.out.println("\nNon-stream implementation demos");
764 System.out.println("===============================");
765
766
767 //
768 // test CMS EnvelopedData
769 //
770 System.out.println("\nCMS EnvelopedData demo [create]:\n");
771 encoding = createEnvelopedData(message, keyEA, keyWrapAlg, kekLength);
772 // transmit data
773 System.out.println("\nCMS EnvelopedData demo [parse]:\n");
774 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
775 parseEnvelopedDataWithRecipientInfoIndex(false, encoding);
776 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
777 parseEnvelopedDataWithRecipientIdentifier(false, encoding);
778
779
780
781 } catch (Exception ex) {
782 ex.printStackTrace();
783 throw new RuntimeException(ex.toString());
784 }
785 }
786
787 /**
788 * Main method.
789 *
790 * @throws IOException
791 * if an I/O error occurs when reading required keys
792 * and certificates from the keystore file
793 */
794 public static void main(String argv[]) throws Exception {
795
796 DemoUtil.initDemos();
797 ECCDemoUtil.installIaikEccProvider();
798 (new EdDHEnvelopedDataDemo()).start();
799 System.out.println("\nReady!");
800 System.in.read();
801 }
802 }