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