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/EdDHAuthEnvelopedDataDemo.java 5 12.02.25 17:58 Dbratko $
059 // $Revision: 5 $
060 //
061
062
063 package demo.cms.ecc;
064
065 import iaik.asn1.CodingException;
066 import iaik.asn1.ObjectID;
067 import iaik.asn1.structures.AlgorithmID;
068 import iaik.asn1.structures.Attribute;
069 import iaik.cms.AuthEnvelopedData;
070 import iaik.cms.AuthEnvelopedDataOutputStream;
071 import iaik.cms.AuthEnvelopedDataStream;
072 import iaik.cms.CMSAlgorithmID;
073 import iaik.cms.CMSException;
074 import iaik.cms.CertificateIdentifier;
075 import iaik.cms.ContentInfo;
076 import iaik.cms.ContentInfoOutputStream;
077 import iaik.cms.ContentInfoStream;
078 import iaik.cms.EncryptedContentInfo;
079 import iaik.cms.EncryptedContentInfoStream;
080 import iaik.cms.IssuerAndSerialNumber;
081 import iaik.cms.KeyAgreeRecipientInfo;
082 import iaik.cms.KeyIdentifier;
083 import iaik.cms.RecipientInfo;
084 import iaik.cms.RecipientKeyIdentifier;
085 import iaik.cms.attributes.CMSContentType;
086 import iaik.security.random.SecRandom;
087 import iaik.utils.Util;
088 import iaik.x509.X509Certificate;
089
090 import java.io.ByteArrayInputStream;
091 import java.io.ByteArrayOutputStream;
092 import java.io.IOException;
093 import java.io.InputStream;
094 import java.security.InvalidKeyException;
095 import java.security.Key;
096 import java.security.NoSuchAlgorithmException;
097 import java.security.PrivateKey;
098 import java.security.SecureRandom;
099
100 import javax.crypto.SecretKey;
101
102 import demo.DemoUtil;
103 import demo.cms.ecc.keystore.CMSEccKeyStore;
104
105
106 /**
107 * Demonstrates the usage of class {@link iaik.cms.AuthEnvelopedDataStream},
108 * {@link iaik.cms.AuthEnvelopedData} and {@link iaik.cms.AuthEnvelopedDataOutputStream}
109 * for authenticated encrypting data with the CMS content type AuthEnvelopedData using the
110 * Elliptic Curve Diffie-Hellman (ECDH) key agreement algorithm with curve25519 and
111 * curve448 according to <a href = "http://www.ietf.org/rfc/rfc5083.txt" target="_blank">RFC 5083</a>
112 * and <a href = "http://www.ietf.org/rfc/rfc8418.txt" target="_blank">RFC 8418</a>.
113 * <p>
114 * This demo uses the AES-CCM and AES-GCM authenticated encryption algorithms
115 * as specified by <a href = "http://www.ietf.org/rfc/rfc5084.txt" target="_blank">RFC 5084</a>.
116 * The demo creates an AuthEnvelopedData object and subsequently shows several
117 * ways that may be used for decrypting the content and verifying the message
118 * authentication code for some particular recipient.
119 * <br>
120 * Since AES-CCM and AES-GCM are not implemented by IAIK-JCE versions prior 3.17, this demo
121 * at least may require IAIK-JCE 3.17 as cryptographic service provider.
122 * <p>
123 * Any keys/certificates required for this demo are read from a keystore
124 * file "cmsecc.keystore" located in your current working directory. If
125 * the keystore file does not exist you can create it by running the
126 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
127 * program.
128 * <p>
129 * Additionally to <code>iaik_cms.jar</code> you also must have
130 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
131 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
132 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
133 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
134 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
135 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
136 * in your classpath.
137 *
138 * @see iaik.cms.AuthEnvelopedDataStream
139 * @see iaik.cms.AuthEnvelopedData
140 * @see iaik.cms.AuthEnvelopedDataOutputStream
141 * @see iaik.cms.RecipientInfo
142 * @see iaik.cms.KeyAgreeRecipientInfo
143 */
144 public class EdDHAuthEnvelopedDataDemo {
145
146 //certificate of x25519 user
147 X509Certificate x25519User;
148 // private key of x25519 user
149 PrivateKey x25519User_pk;
150 // certificate of x448 user
151 X509Certificate x448User;
152 // private key of x448 user
153 PrivateKey x448User_pk;
154
155 // secure random number generator
156 SecureRandom random;
157
158 /**
159 * Setup the demo certificate chains.
160 *
161 * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore")
162 * file which has to be located in your current working directory and may be
163 * created by running {@link demo.keystore.SetupCMSKeyStore
164 * SetupCMSKeyStore}.
165 *
166 * @throws IOException if an file read error occurs
167 */
168 public EdDHAuthEnvelopedDataDemo() throws IOException {
169
170 System.out.println();
171 System.out.println("***************************************************************************************************");
172 System.out.println("* EdDHAuthEnvelopedDataDemo *");
173 System.out.println("* (shows the usage of the CMS AuthEnvelopedData type implementation with curve25519 and curve448) *");
174 System.out.println("***************************************************************************************************");
175 System.out.println();
176
177 // get keys and certificate from the demo keystore
178 x25519User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519)[0];
179 x25519User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X25519);
180 x448User = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448)[0];
181 x448User_pk = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_X448);
182 random = SecRandom.getDefault();
183
184 }
185
186
187 /**
188 * Creates a CMS <code>AuthEnvelopedDataStream</code> message.
189 *
190 * @param message the message to be authenticated-enveloped, as byte representation
191 * @param keyEA the key encryption (key agreement) algorithm used for creating
192 * a shared key encryption key for encrypting the secret content
193 * encryption key with it
194 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
195 * the content encryption key
196 * @param kekLength the length of the key encryption key to be created for
197 * encrypting the content encryption key with it
198 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
199 *
200 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
201 *
202 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
203 * be created
204 * @throws IOException if an I/O error occurs
205 */
206 public byte[] createAuthEnvelopedDataStream(byte[] message,
207 AlgorithmID keyEA,
208 AlgorithmID keyWrapAlg,
209 int kekLength,
210 AlgorithmID contentAuthEncAlg)
211 throws CMSException, IOException {
212
213 // we are testing the stream interface
214 ByteArrayInputStream is = new ByteArrayInputStream(message);
215 // create a new AuthEnvelopedData object
216 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is, contentAuthEncAlg);
217
218 if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) ||
219 contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
220 contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
221 // for aes-ccm we need to know the data input length in advance
222 authEnvelopedData.setInputLength(message.length);
223 }
224
225 // create some authenticated attributes
226 try {
227 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
228 authEnvelopedData.setAuthenticatedAttributes(attributes);
229 } catch (Exception ex) {
230 throw new CMSException("Error creating attribute: " + ex.toString());
231 }
232
233 // create the recipient infos
234 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
235 // specify the recipients of the encrypted message
236 authEnvelopedData.setRecipientInfos(recipients);
237
238 // return the AuthEnvelopedDate as BER encoded byte array with block size 16
239 // (just for testing; in real application we will use a proper blocksize,
240 // e.g. 2048, 4096,..)
241 authEnvelopedData.setBlockSize(16);
242 // wrap into ContentInfo
243 ContentInfoStream contentInfo = new ContentInfoStream(authEnvelopedData);
244 ByteArrayOutputStream os = new ByteArrayOutputStream();
245 contentInfo.writeTo(os);
246 return os.toByteArray();
247 }
248
249 /**
250 * Creates a CMS <code>AuthEnvelopedData</code> message using the
251 * {@link iaik.cms.AuthEnvelopedDataOutputStream AuthEnvelopedDataOutputStream}
252 * class.
253 *
254 * @param message the message to be authenticated-enveloped, as byte representation
255 * @param keyEA the key encryption (key agreement) algorithm used for creating
256 * a shared key encryption key for encrypting the secret content
257 * encryption key with it
258 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
259 * the content encryption key
260 * @param kekLength the length of the key encryption key to be created for
261 * encrypting the content encryption key with it
262 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
263 *
264 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
265 *
266 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
267 * be created
268 * @throws IOException if an I/O error occurs
269 */
270 public byte[] createAuthEnvelopedDataOutputStream(byte[] message,
271 AlgorithmID keyEA,
272 AlgorithmID keyWrapAlg,
273 int kekLength,
274 AlgorithmID contentAuthEncAlg)
275 throws CMSException, IOException {
276
277 // a stream from which to read the data to be encrypted
278 ByteArrayInputStream is = new ByteArrayInputStream(message);
279
280 // the stream to which to write the AuthEnvelopedData
281 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
282
283 // wrap AuthEnvelopedData into a ContentInfo
284 ContentInfoOutputStream contentInfoStream =
285 new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
286
287 // create a new AuthEnvelopedData object
288 AuthEnvelopedDataOutputStream authEnvelopedData =
289 new AuthEnvelopedDataOutputStream(contentInfoStream, contentAuthEncAlg);
290
291 if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) ||
292 contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) ||
293 contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) {
294 // for aes-ccm we need to know the data input length in advance
295 authEnvelopedData.setInputLength(message.length);
296 }
297
298 // create some authenticated attributes
299 try {
300 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
301 authEnvelopedData.setAuthenticatedAttributes(attributes);
302 } catch (Exception ex) {
303 throw new CMSException("Error creating attribute: " + ex.toString());
304 }
305
306 // create the recipient infos
307 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
308 // specify the recipients of the encrypted message
309 authEnvelopedData.setRecipientInfos(recipients);
310
311 int blockSize = 16; // in real world we would use a block size like 2048
312 // write in the data to be encrypted
313 byte[] buffer = new byte[blockSize];
314 int bytesRead;
315 while ((bytesRead = is.read(buffer)) != -1) {
316 authEnvelopedData.write(buffer, 0, bytesRead);
317 }
318
319 // closing the stream finishes encryption and closes the underlying stream
320 authEnvelopedData.close();
321 return resultStream.toByteArray();
322 }
323
324
325 /**
326 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
327 * the recipient identified by its index into the recipientInfos field and verifies
328 * the message authentication code.
329 * <p>
330 * This way of decrypting the content may be used for any type of RecipientInfo
331 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
332 * know at what index of the recipientInfo field the RecipientInfo for the
333 * particular recipient in mind can be found. If the recipient in mind uses
334 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
335 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
336 * keys for more than only one recipient; since the recipientInfoIndex only
337 * specifies the RecipientInfo but not the encrypted content encryption key
338 * -- if there are more than only one -- repeated decryption runs may be
339 * required as long as the decryption process completes successfully.
340 *
341 * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
342 * @param key the key to decrypt the message
343 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
344 * to which the specified key belongs
345 *
346 * @return the recovered message, as byte array
347 * @throws CMSException if the message cannot be recovered or MAC verification fails
348 * @throws IOException if a stream read/write error occurs
349 */
350 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
351 throws CMSException, IOException {
352
353 // create the AuthEnvelopedData object from a BER encoded byte array
354 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
355 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
356
357 System.out.println("Information about the authenticated encrypted data:");
358 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
359 System.out.println("Content type: "+eci.getContentType().getName());
360 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
361
362 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
363 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
364
365 // for demonstration purposes we only look one time for all recipients included:
366 if (recipientInfoIndex == 0) {
367 int k = 0;
368 for (int i=0; i<recipients.length; i++) {
369 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
370 for (int j = 0; j < recipientIDs.length; j++) {
371 System.out.println("Recipient "+(++k)+":");
372 System.out.println(recipientIDs[j]);
373 }
374 }
375 }
376 // decrypt the message for the first recipient and verify mac
377 try {
378 authEnvelopedData.setupCipher(key, recipientInfoIndex);
379 InputStream decrypted = authEnvelopedData.getInputStream();
380 ByteArrayOutputStream os = new ByteArrayOutputStream();
381 Util.copyStream(decrypted, os, null);
382 byte[] content = os.toByteArray();
383
384 // get authenticated attributes
385 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
386 if (contentTypeAttribute != null) {
387 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
388 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
389 }
390
391 return content;
392 } catch (InvalidKeyException ex) {
393 throw new CMSException("Private key error: "+ex.toString());
394 } catch (NoSuchAlgorithmException ex) {
395 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
396 } catch (CodingException ex) {
397 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
398 }
399 }
400
401 /**
402 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
403 * the recipient identified by recipient identifier and verifies the message
404 * authentication code.
405 * <p>
406 * This way of decrypting the content may be used for any type of RecipientInfo
407 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The
408 * recipient in mind is identified by its recipient identifier.
409 *
410 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
411 * @param key the key to decrypt the message
412 * @param recipientID the recipient identifier uniquely identifying the key of the
413 * recipient
414 *
415 * @return the recovered message, as byte array
416 * @throws CMSException if the message cannot be recovered
417 * @throws IOException if a stream read/write error occurs
418 */
419 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
420 throws CMSException, IOException {
421
422 // create the AuthEnvelopedData object from a DER encoded byte array
423 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
424 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
425
426 System.out.println("Information about the encrypted data:");
427 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
428 System.out.println("Content type: "+eci.getContentType().getName());
429 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
430
431 // get the right RecipientInfo
432 System.out.println("\nSearch for RecipientInfo:");
433 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
434 if (recipient != null) {
435 System.out.println("RecipientInfo: " + recipient);
436 } else {
437 throw new CMSException("No recipient with ID: " + recipientID);
438 }
439 // decrypt the content encryption key and the content; verify mac
440 try {
441 System.out.println("Decrypt encrypted content encryption key...");
442 SecretKey cek = recipient.decryptKey(key, recipientID);
443 System.out.println("Decrypt content with decrypted content encryption key...");
444 authEnvelopedData.setupCipher(cek);
445 InputStream decrypted = authEnvelopedData.getInputStream();
446 ByteArrayOutputStream os = new ByteArrayOutputStream();
447 Util.copyStream(decrypted, os, null);
448 byte[] content = os.toByteArray();
449
450 // get authenticated attributes
451 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
452 if (contentTypeAttribute != null) {
453 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
454 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
455 }
456
457 return content;
458 } catch (InvalidKeyException ex) {
459 throw new CMSException("Private key error: "+ex.toString());
460 } catch (NoSuchAlgorithmException ex) {
461 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
462 } catch (CodingException ex) {
463 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
464 }
465 }
466
467 /**
468 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
469 * the recipient identified by its recipient certificate and verifies the message
470 * authentication code.
471 *
472 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
473 * @param key the key to decrypt the message
474 * @param recipientCert the certificate of the recipient
475 *
476 * @return the recovered message, as byte array
477 * @throws CMSException if the message cannot be recovered
478 * @throws IOException if a stream read/write error occurs
479 */
480 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert)
481 throws CMSException, IOException {
482
483 // create the AuthEnvelopedData object from a BER encoded byte array
484 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
485 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
486
487 System.out.println("Information about the encrypted data:");
488 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
489 System.out.println("Content type: "+eci.getContentType().getName());
490 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
491
492 // decrypt the content encryption key and the content; verify mac
493 try {
494 System.out.println("Decrypt the content...");
495 authEnvelopedData.setupCipher(key, recipientCert);
496 InputStream decrypted = authEnvelopedData.getInputStream();
497 ByteArrayOutputStream os = new ByteArrayOutputStream();
498 Util.copyStream(decrypted, os, null);
499 byte[] content = os.toByteArray();
500
501 // get authenticated attributes
502 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
503 if (contentTypeAttribute != null) {
504 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
505 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
506 }
507
508 return content;
509 } catch (InvalidKeyException ex) {
510 throw new CMSException("Private key error: "+ex.toString());
511 } catch (NoSuchAlgorithmException ex) {
512 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
513 } catch (CodingException ex) {
514 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
515 }
516 }
517
518
519 // non stream
520
521 /**
522 * Creates a CMS <code>AuthEnvelopedData</code> message.
523 *
524 * @param message the message to be enveloped, as byte representation
525 * @param keyEA the key encryption (key agreement) algorithm used for creating
526 * a shared key encryption key for encrypting the secret content
527 * encryption key with it
528 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
529 * the content encryption key
530 * @param kekLength the length of the key encryption key to be created for
531 * encrypting the content encryption key with it
532 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
533 *
534 *
535 * @return the encoded <code>AuthEnvelopedData</code>, as byte array
536 *
537 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
538 * be created
539 */
540 public byte[] createAuthEnvelopedData(byte[] message, AlgorithmID keyEA, AlgorithmID keyWrapAlg,
541 int kekLength, AlgorithmID contentAuthEncAlg)
542 throws CMSException {
543
544 AuthEnvelopedData authEnvelopedData;
545
546 // create a new AuthEnvelopedData object
547 authEnvelopedData = new AuthEnvelopedData(message, contentAuthEncAlg);
548
549 // create some authenticated attributes
550 try {
551 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
552 authEnvelopedData.setAuthenticatedAttributes(attributes);
553 } catch (Exception ex) {
554 throw new CMSException("Error creating attribute: " + ex.toString());
555 }
556
557 // set the RecipientInfos
558 RecipientInfo[] recipients = createRecipients(keyEA, keyWrapAlg, kekLength);
559 authEnvelopedData.setRecipientInfos(recipients);
560
561 // wrap into ContentInfo
562 ContentInfo contentInfo = new ContentInfo(authEnvelopedData);
563 // return encoded EnvelopedData
564 return contentInfo.getEncoded();
565 }
566
567
568 /**
569 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
570 * the recipient identified by its index into the recipientInfos field and verifies
571 * the message authentication code.
572 * <p>
573 * This way of decrypting the content may be used for any type of RecipientInfo
574 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
575 * know at what index of the recipientInfo field the RecipientInfo for the
576 * particular recipient in mind can be found. If the recipient in mind uses
577 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
578 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
579 * keys for more than only one recipient; since the recipientInfoIndex only
580 * specifies the RecipientInfo but not the encrypted content encryption key
581 * -- if there are more than only one -- repeated decryption runs may be
582 * required as long as the decryption process completes successfully.
583 *
584 * @param enc the encoded <code>AuthEnvelopedData</code>
585 *
586 * @param key the key to decrypt the message
587 *
588 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
589 * to which the specified key belongs
590 *
591 * @return the recovered message, as byte array
592 *
593 * @throws CMSException if the message cannot be recovered
594 * @throws IOException if an I/O error occurs
595 */
596 public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex)
597 throws CMSException, IOException {
598 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
599 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
600
601 System.out.println("Information about the encrypted data:");
602 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
603 System.out.println("Content type: "+eci.getContentType().getName());
604 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
605
606 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
607 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
608
609 // for demonstration purposes we only look one time for all recipients included:
610 if (recipientInfoIndex == 0) {
611 int k = 0;
612 for (int i=0; i<recipients.length; i++) {
613 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
614 for (int j = 0; j < recipientIDs.length; j++) {
615 System.out.println("Recipient "+(++k)+":");
616 System.out.println(recipientIDs[j]);
617 }
618 }
619 }
620
621 // decrypt the message and verify the mac
622 try {
623 authEnvelopedData.setupCipher(key, recipientInfoIndex);
624 byte[] content = authEnvelopedData.getContent();
625
626 // get authenticated attributes
627 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
628 if (contentTypeAttribute != null) {
629 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
630 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
631 }
632
633 return content;
634 } catch (InvalidKeyException ex) {
635 throw new CMSException("Private key error: "+ex.toString());
636 } catch (NoSuchAlgorithmException ex) {
637 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
638 } catch (CodingException ex) {
639 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
640 }
641 }
642
643 /**
644 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
645 * the recipient identified by recipient identifier.
646 * <p>
647 *
648 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
649 * @param key the key to decrypt the message
650 * @param recipientID the recipient identifier uniquely identifying the key of the
651 * recipient
652 *
653 * @return the recovered message, as byte array
654 * @throws CMSException if the message cannot be recovered
655 * @throws IOException if an I/O error occurs
656 */
657 public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID)
658 throws CMSException, IOException {
659 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
660 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
661
662 System.out.println("Information about the encrypted data:");
663 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
664 System.out.println("Content type: "+eci.getContentType().getName());
665 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
666
667 // get the right RecipientInfo
668 System.out.println("\nSearch for RecipientInfo:");
669 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
670 if (recipient != null) {
671 System.out.println("RecipientInfo: " + recipient);
672 } else {
673 throw new CMSException("No recipient with ID: " + recipientID);
674 }
675 // decrypt the content encryption key and the content
676 try {
677 System.out.println("Decrypt encrypted content encryption key...");
678 SecretKey cek = recipient.decryptKey(key, recipientID);
679 System.out.println("Decrypt content with decrypted content encryption key...");
680 // decrypt content and verify mac
681 authEnvelopedData.setupCipher(cek);
682 byte[] content = authEnvelopedData.getContent();
683
684 // get authenticated attributes
685 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
686 if (contentTypeAttribute != null) {
687 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
688 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
689 }
690
691 return content;
692 } catch (InvalidKeyException ex) {
693 throw new CMSException("Private key error: "+ex.toString());
694 } catch (NoSuchAlgorithmException ex) {
695 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
696 } catch (CodingException ex) {
697 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
698 }
699 }
700
701 /**
702 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
703 * the recipient identified by its recipient certificate or keyID.
704 *
705 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
706 * @param key the key to decrypt the message
707 * @param recipientCert the certificate of the recipient
708 *
709 * @return the recovered message, as byte array
710 * @throws CMSException if the message cannot be recovered
711 */
712 public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert)
713 throws CMSException, IOException {
714 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
715 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
716
717 System.out.println("Information about the encrypted data:");
718 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
719 System.out.println("Content type: "+eci.getContentType().getName());
720 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
721
722 // decrypt the content encryption key and the content
723 try {
724 System.out.println("Decrypt the content and verify mac...");
725 // decrypt content and verify mac
726 authEnvelopedData.setupCipher(key, recipientCert);
727
728 byte[] content = authEnvelopedData.getContent();
729
730 // get authenticated attributes
731 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
732 if (contentTypeAttribute != null) {
733 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
734 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
735 }
736
737 return content;
738 } catch (InvalidKeyException ex) {
739 throw new CMSException("Private key error: "+ex.toString());
740 } catch (NoSuchAlgorithmException ex) {
741 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
742 } catch (CodingException ex) {
743 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
744 }
745
746 }
747
748 /**
749 * Creates the RecipientInfos.
750 *
751 * @param keyEA the key encryption (key agreement) algorithm used for creating
752 * a shared key encryption key for encrypting the secret content
753 * encryption key with it
754 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
755 * the content encryption key
756 * @param kekLength the length of the key encryption key to be created for
757 * encrypting the content encryption key with it
758 *
759 * @return the RecipientInfos created, two KeyAgreeRecipientInfos
760 *
761 * @throws CMSException if an error occurs when creating the recipient infos
762 */
763 public RecipientInfo[] createRecipients(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength) throws CMSException {
764 // just for demonstration we use two recipients, one having a x25519 key, the other a x448 key
765 RecipientInfo[] recipients = new RecipientInfo[2];
766 try {
767 recipients[0] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
768 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(x25519User, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
769
770 recipients[1] = new KeyAgreeRecipientInfo((AlgorithmID)keyEA.clone(), (AlgorithmID)keyWrapAlg.clone(), kekLength);
771 ((KeyAgreeRecipientInfo)recipients[1]).addRecipient(x448User, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
772
773 } catch (Exception ex) {
774 throw new CMSException("Error adding recipients: " + ex.toString());
775 }
776 return recipients;
777 }
778
779 /**
780 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
781 * using the index into the recipientInfos field for identifying the recipient.
782 *
783 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
784 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
785 *
786 * @throws Exception if some error occurs during decoding/decryption
787 */
788 public void parseAuthEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
789 byte[] receivedMessage;
790 if (stream) {
791 // x25519User
792 System.out.println("\nDecrypt for x25519User:");
793 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, 0);
794 System.out.print("\nDecrypted content: ");
795 System.out.println(new String(receivedMessage));
796 // x448User
797 System.out.println("\nDecrypt for x448User:");
798 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, 1);
799 System.out.print("\nDecrypted content: ");
800 System.out.println(new String(receivedMessage));
801
802 } else {
803 // x25519User
804 System.out.println("\nDecrypt for x25519User:");
805 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, 0);
806 System.out.print("\nDecrypted content: ");
807 System.out.println(new String(receivedMessage));
808 // x448User
809 System.out.println("\nDecrypt for x448User:");
810 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, 1);
811 System.out.print("\nDecrypted content: ");
812 System.out.println(new String(receivedMessage));
813
814 }
815 }
816
817 /**
818 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
819 * using their recipient identifiers for identifying the recipient.
820 *
821 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
822 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
823 *
824 * @throws Exception if some error occurs during decoding/decryption
825 */
826 public void parseAuthEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
827 byte[] receivedMessage;
828 if (stream) {
829 // x25519User
830 System.out.println("\nDecrypt for x25519User:");
831 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
832 System.out.print("\nDecrypted content: ");
833 System.out.println(new String(receivedMessage));
834 // x448User
835 System.out.println("\nDecrypt for x448User:");
836 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
837 System.out.print("\nDecrypted content: ");
838 System.out.println(new String(receivedMessage));
839 } else {
840 // x25519User
841 System.out.println("\nDecrypt for x25519User:");
842 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, new IssuerAndSerialNumber(x25519User));
843 System.out.print("\nDecrypted content: ");
844 System.out.println(new String(receivedMessage));
845 // x448User
846 System.out.println("\nDecrypt for x448User:");
847 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, new RecipientKeyIdentifier(x448User));
848 System.out.print("\nDecrypted content: ");
849 System.out.println(new String(receivedMessage));
850 }
851 }
852
853 /**
854 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
855 * using their recipient certificate for identifying the recipient.
856 *
857 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
858 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
859 *
860 * @throws Exception if some error occurs during decoding/decryption
861 */
862 public void parseAuthEnvelopedDataWithRecipientCert(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception {
863 byte[] receivedMessage;
864 if (stream) {
865 // x25519User
866 System.out.println("\nDecrypt for x25519User:");
867 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x25519User_pk, x25519User);
868 System.out.print("\nDecrypted content: ");
869 System.out.println(new String(receivedMessage));
870 // x448User
871 System.out.println("\nDecrypt for x448User:");
872 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, x448User_pk, x448User);
873 System.out.print("\nDecrypted content: ");
874 System.out.println(new String(receivedMessage));
875 } else {
876 // x25519User
877 System.out.println("\nDecrypt for x25519User:");
878 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x25519User_pk, x25519User);
879 System.out.print("\nDecrypted content: ");
880 System.out.println(new String(receivedMessage));
881 // x448User
882 System.out.println("\nDecrypt for x448User:");
883 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, x448User_pk, x448User);
884 System.out.print("\nDecrypted content: ");
885 System.out.println(new String(receivedMessage));
886 }
887 }
888
889 /**
890 * Starts the test.
891 */
892 public void start() {
893
894 AlgorithmID[] keyEAs = {
895 AlgorithmID.dhSinglePass_stdDH_sha256kdf_scheme,
896 AlgorithmID.dhSinglePass_stdDH_sha384kdf_scheme,
897 AlgorithmID.dhSinglePass_stdDH_hkdf_sha256_scheme,
898 AlgorithmID.dhSinglePass_stdDH_hkdf_sha384_scheme,
899 AlgorithmID.dhSinglePass_stdDH_hkdf_sha512_scheme,
900 };
901
902 AlgorithmID[] keyWrapAlgs = {
903 CMSAlgorithmID.cms_aes128_wrap,
904 CMSAlgorithmID.cms_aes192_wrap,
905 CMSAlgorithmID.cms_aes256_wrap,
906 };
907
908 for (int i = 0; i < keyEAs.length; i++) {
909 AlgorithmID[] contentEAs = { AlgorithmID.aes256_GCM, AlgorithmID.aes256_CCM, AlgorithmID.chacha20Poly1305 };
910 AlgorithmID keyEA = keyEAs[i];
911 for (int j = 0; j < keyWrapAlgs.length; j++) {
912 AlgorithmID keyWrapAlg = keyWrapAlgs[j];
913 int kekLength = 256;
914 if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes192_wrap)) {
915 kekLength = 192;
916 contentEAs = new AlgorithmID[] { AlgorithmID.aes192_GCM, AlgorithmID.aes192_CCM };
917 } else if (keyWrapAlg.equals(CMSAlgorithmID.cms_aes128_wrap)) {
918 kekLength = 128;
919 contentEAs = new AlgorithmID[] { AlgorithmID.aes128_GCM, AlgorithmID.aes128_CCM };
920 }
921
922 for (int k = 0; k < contentEAs.length; k++) {
923 AlgorithmID contentAuthEncAlg = (AlgorithmID)contentEAs[k].clone();
924 System.out.println("EdDH AuthEnveloped demo for " + keyEA.getName() + " with " + keyWrapAlg.getName() +" and " + contentAuthEncAlg.getName());
925 start(keyEA, keyWrapAlg, kekLength, contentAuthEncAlg);
926 }
927 }
928 }
929 }
930
931 /**
932 * Starts the test for the given content-authenticated encryption algorithm.
933 *
934 * @param keyEA the key encryption (key agreement) algorithm used for creating
935 * a shared key encryption key for encrypting the secret content
936 * encryption key with it
937 * @param keyWrapAlg the key wrap algorithm to be used for wrapping (encrypting)
938 * the content encryption key
939 * @param kekLength the length of the key encryption key to be created for
940 * encrypting the content encryption key with it
941 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
942 */
943 public void start(AlgorithmID keyEA, AlgorithmID keyWrapAlg, int kekLength, AlgorithmID contentAuthEncAlg) {
944 // the test message
945 String m = "This is the test message.";
946 System.out.println("Test message: \""+m+"\"");
947 System.out.println();
948 byte[] message = m.getBytes();
949
950 try {
951 byte[] encodedAuthEnvelopedData;
952 System.out.println("Stream implementation demos");
953 System.out.println("===========================");
954
955 // the stream implementation
956 //
957 // test CMS AuthEnvelopedDataStream
958 //
959 System.out.println("\nCMS AuthEnvelopedDataStream demo [create]:\n");
960 encodedAuthEnvelopedData = createAuthEnvelopedDataStream(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
961 // transmit data
962 System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
963 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
964 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData);
965 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
966 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData);
967 System.out.println("Decrypt for the several recipients using their certificate.");
968 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData);
969
970
971 // the output stream implementation
972 //
973 // test CMS AuthEnvelopedDataOutputStream
974 //
975 System.out.println("\nCMS AuthEnvelopedDataOutputStream demo [create]:\n");
976 encodedAuthEnvelopedData = createAuthEnvelopedDataOutputStream(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
977 // transmit data
978 System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n");
979 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
980 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData);
981 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
982 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData);
983 System.out.println("Decrypt for the several recipients using their certificate.");
984 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData);
985
986 // the non-stream implementation
987 System.out.println("\nNon-stream implementation demos");
988 System.out.println("===============================");
989
990
991 //
992 // test CMS AuthEnvelopedData
993 //
994 System.out.println("\nCMS AuthEnvelopedData demo [create]:\n");
995 encodedAuthEnvelopedData = createAuthEnvelopedData(message, keyEA, keyWrapAlg, kekLength, (AlgorithmID)contentAuthEncAlg.clone());
996 // transmit data
997 System.out.println("\nCMS AuthEnvelopedData demo [parse]:\n");
998 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
999 parseAuthEnvelopedDataWithRecipientInfoIndex(false, encodedAuthEnvelopedData);
1000 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1001 parseAuthEnvelopedDataWithRecipientIdentifier(false, encodedAuthEnvelopedData);
1002 System.out.println("Decrypt for the several recipients using their certificate.");
1003 parseAuthEnvelopedDataWithRecipientCert(false, encodedAuthEnvelopedData);
1004
1005
1006 } catch (Exception ex) {
1007 ex.printStackTrace();
1008 throw new RuntimeException(ex.toString());
1009 }
1010 }
1011
1012
1013 /**
1014 * Main method.
1015 *
1016 * @throws IOException
1017 * if an I/O error occurs when reading required keys
1018 * and certificates from files
1019 */
1020 public static void main(String argv[]) throws Exception {
1021
1022 DemoUtil.initDemos();
1023 ECCDemoUtil.installIaikEccProvider();
1024
1025 (new EdDHAuthEnvelopedDataDemo()).start();
1026 System.out.println("\nReady!");
1027 DemoUtil.waitKey();
1028 }
1029
1030 }