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