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/authenticatedData/AuthenticatedDataOutputStreamDemo.java 19 12.02.25 17:58 Dbratko $
059 // $Revision: 19 $demo.cms.authenticatedData
060 package demo.cms.authenticatedData;
061
062 import iaik.asn1.ObjectID;
063 import iaik.asn1.structures.AlgorithmID;
064 import iaik.asn1.structures.Attribute;
065 import iaik.cms.AuthenticatedDataOutputStream;
066 import iaik.cms.AuthenticatedDataStream;
067 import iaik.cms.CMSAlgorithmID;
068 import iaik.cms.CMSException;
069 import iaik.cms.CertificateIdentifier;
070 import iaik.cms.ContentInfoOutputStream;
071 import iaik.cms.IssuerAndSerialNumber;
072 import iaik.cms.KEKIdentifier;
073 import iaik.cms.KEKRecipientInfo;
074 import iaik.cms.KeyAgreeRecipientInfo;
075 import iaik.cms.KeyIdentifier;
076 import iaik.cms.KeyTransRecipientInfo;
077 import iaik.cms.OriginatorInfo;
078 import iaik.cms.RecipientInfo;
079 import iaik.cms.RecipientKeyIdentifier;
080 import iaik.cms.SecurityProvider;
081 import iaik.cms.SubjectKeyID;
082 import iaik.cms.attributes.CMSContentType;
083 import iaik.security.random.SecRandom;
084 import iaik.utils.Util;
085 import iaik.x509.X509Certificate;
086
087 import java.io.ByteArrayInputStream;
088 import java.io.ByteArrayOutputStream;
089 import java.io.IOException;
090 import java.io.InputStream;
091 import java.security.InvalidKeyException;
092 import java.security.Key;
093 import java.security.NoSuchAlgorithmException;
094 import java.security.PrivateKey;
095 import java.security.SecureRandom;
096
097 import javax.crypto.KeyGenerator;
098 import javax.crypto.SecretKey;
099
100 import demo.DemoUtil;
101 import demo.keystore.CMSKeyStore;
102
103 /**
104 * Demonstrates the usage of class {@link iaik.cms.AuthenticatedDataOutputStream} and
105 * {@link iaik.cms.AuthenticatedDataOutputStream} for recipient-specific protecting the
106 * integrity of message using the CMS type AuthenticatedData.
107 * <p>
108 * <b>Attention:</b> This demo uses Static-Static Diffie-Hellman as key management
109 * technique for providing origin authentication.
110 * <p>
111 * This demo requires that you have <code>iaik_esdh.jar</code>
112 * (or <code>iaik_jce_full.jar</code>) in your classpath.
113 * You can download it from <a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
114 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>.
115 *
116 *
117 * @see iaik.cms.AuthenticatedDataStream
118 * @see iaik.cms.AuthenticatedDataOutputStream
119 *
120 */
121 public class AuthenticatedDataOutputStreamDemo {
122
123 // certificate of rsaUser 1
124 X509Certificate rsaUser1;
125 // private key of rsaUser 1
126 PrivateKey rsaUser1_pk;
127 // certificate of rsaUser 2
128 X509Certificate rsaUser2;
129 // private key of rsaUser 2
130 PrivateKey rsaUser2_pk;
131
132 // certificate of (originator) SSDH User 1 (static-static Diffie-Hellman)
133 X509Certificate ssdhUser1;
134 X509Certificate[] originatorCerts;
135 // private key of SSDH User 1
136 PrivateKey ssdhUser1_pk;
137 // certificate of SSDH User 2 (static-static Diffie-Hellman)
138 X509Certificate ssdhUser2;
139 // private key of SSDH User 2
140 PrivateKey ssdhUser2_pk;
141
142 // key encryption key for KEKRecipientInfo
143 SecretKey kek;
144 byte[] kekID;
145
146
147 // secure random number generator
148 SecureRandom random;
149
150 /**
151 * Setup the demo certificate chains.
152 *
153 * Keys and certificate are retrieved from the demo KeyStore which
154 * has to be located in your current working directory and may be
155 * created by running {@link demo.keystore.SetupCMSKeyStore
156 * SetupCMSKeyStore}.
157 *
158 * @throws IOException if an file read error occurs
159 */
160 public AuthenticatedDataOutputStreamDemo() throws IOException {
161
162 System.out.println();
163 System.out.println("**********************************************************************************");
164 System.out.println("* AuthenticatedDataOutputStream demo *");
165 System.out.println("* (shows the usage of the CMS AuthenticatedDataOutputStream implementation) *");
166 System.out.println("**********************************************************************************");
167 System.out.println();
168
169 // add all certificates to the list
170 rsaUser1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
171 rsaUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
172 rsaUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
173 rsaUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
174
175 originatorCerts = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1);
176 ssdhUser1 = originatorCerts[0];
177 ssdhUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1);
178 ssdhUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2)[0];
179 ssdhUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2);
180
181 random = SecRandom.getDefault();
182
183 // create a secret key encryption key for a KEKRecipientInfo
184 KeyGenerator kg;
185 try {
186 kg = SecurityProvider.getSecurityProvider().getKeyGenerator("3DES", 192);
187 } catch (NoSuchAlgorithmException ex) {
188 throw new IOException("KeyGenerator for 3DES not avcailable!");
189 }
190 kek = kg.generateKey();
191 kekID = new byte[] { 00, 00, 00, 01 };
192
193 }
194
195
196 /**
197 * Creates a CMS <code>AuthenticatedDataOutputStream</code> for the given message message.
198 *
199 * @param message the message to be authenticated, as byte representation
200 * @param macAlgorithm the mac algorithm to be used
201 * @param macKeyLength the length of the temporary MAC key to be generated
202 * @param digestAlgorithm the digest algorithm to be used to calculate a digest
203 * from the content if authenticated attributes should
204 * be included
205 * @param mode whether to include the content into the AuthenticatedData ({@link
206 * AuthenticatedDataStream#IMPLICIT implicit}) or to not include it
207 * ({@link AuthenticatedDataStream#EXPLICIT explicit})
208 *
209 * @return the BER encoding of the <code>AuthenticatedData</code> object just created,
210 * wrapped into a ContentInfo
211 *
212 * @throws CMSException if the <code>AuthenticatedData</code> object cannot
213 * be created
214 * @throws IOException if an I/O error occurs
215 */
216 public byte[] createAuthenticatedDataStream(byte[] message,
217 AlgorithmID macAlgorithm,
218 int macKeyLength,
219 AlgorithmID digestAlgorithm,
220 int mode)
221 throws CMSException, IOException {
222
223 AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone();
224 AlgorithmID digestAlg = null;
225 if (digestAlgorithm != null) {
226 digestAlg = (AlgorithmID)digestAlgorithm.clone();
227 }
228 ObjectID contentType = ObjectID.cms_data;
229
230 AuthenticatedDataOutputStream authenticatedData;
231
232 // a stream from which to read the data to be authenticated
233 ByteArrayInputStream is = new ByteArrayInputStream(message);
234
235 // the stream to which to write the AuthenticatedData
236 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
237
238 // wrap AuthenticatedData into a ContentInfo
239 ContentInfoOutputStream contentInfoStream =
240 new ContentInfoOutputStream(ObjectID.cms_authData, resultStream);
241
242 // create AuthenticatedDataOutputStream
243 try {
244 authenticatedData = new AuthenticatedDataOutputStream(contentType,
245 contentInfoStream,
246 macAlg,
247 macKeyLength,
248 null,
249 digestAlg,
250 mode);
251 } catch (NoSuchAlgorithmException ex) {
252 throw new CMSException(ex.toString());
253 }
254
255 // static-static mode: set OriginatorInfo
256 OriginatorInfo originator = new OriginatorInfo();
257 originator.setCertificates(originatorCerts);
258 authenticatedData.setOriginatorInfo(originator);
259 // create the recipient infos
260 RecipientInfo[] recipients = createRecipients();
261 // specify the recipients of the authenticated message
262 authenticatedData.setRecipientInfos(recipients);
263
264 if (digestAlgorithm != null) {
265 // create some authenticated attributes
266 // (the message digest attribute is automatically added)
267 try {
268 Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
269 authenticatedData.setAuthenticatedAttributes(attributes);
270 } catch (Exception ex) {
271 throw new CMSException("Error creating attribute: " + ex.toString());
272 }
273 }
274
275 int blockSize = 20; // in real world we would use a block size like 2048
276 // write in the data to be signed
277 byte[] buffer = new byte[blockSize];
278 int bytesRead;
279 while ((bytesRead = is.read(buffer)) != -1) {
280 authenticatedData.write(buffer, 0, bytesRead);
281 }
282
283 // closing the stream adds auth/unauth attributes, calculates and adds the mac value, .
284 authenticatedData.close();
285 return resultStream.toByteArray();
286 }
287
288 /**
289 * Decrypts the encrypted MAC key for the recipient identified by its index
290 * into the recipientInfos field and uses the MAC key to verify
291 * the authenticated data.
292 * <p>
293 * This way of decrypting the MAC key and verifying the content may be used for
294 * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo,
295 * KEKRecipientInfo, PasswordRecipeintInfo, OtherRecipientInfo), but requires to
296 * know at what index of the recipientInfos field the RecipientInfo for the
297 * particular recipient in mind can be found.
298 * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
299 * some processing overhead may take place because a KeyAgreeRecipientInfo may
300 * contain encrypted mac keys for more than only one recipient; since the
301 * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
302 * mac key -- if there are more than only one -- repeated decryption runs may be
303 * required as long as the decryption process completes successfully.
304 *
305 * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
306 * @param message the content message, if transmitted by other means (explicit mode)
307 * @param key the key to decrypt the mac key
308 * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to
309 * which the given key belongs
310 *
311 * @return the verified message, as byte array
312 *
313 * @throws CMSException if the authenticated data cannot be verified
314 * @throws IOException if a stream read/write error occurs
315 */
316 public byte[] getAuthenticatedDataStream(byte[] encoding,
317 byte[] message,
318 Key key,
319 int recipientInfoIndex)
320 throws CMSException, IOException {
321
322 // parse the BER encoded AuthenticatedData
323 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
324 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
325
326 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
327 // in explicit mode explicitly supply the content for hash/mac computation
328 authenticatedData.setInputStream(new ByteArrayInputStream(message));
329 }
330
331 System.out.println("\nThis message can be verified by the following recipients:");
332 RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
333
334 // for demonstration purposes we only look one time for all recipients included:
335 if (recipientInfoIndex == 0) {
336 int k = 0;
337 for (int i=0; i<recipients.length; i++) {
338 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
339 for (int j = 0; j < recipientIDs.length; j++) {
340 System.out.println("Recipient "+(++k)+":");
341 System.out.println(recipientIDs[j]);
342 }
343 }
344 }
345 // decrypt the mac key and verify the mac for the indented recipient
346 try {
347 authenticatedData.setupMac(key, recipientInfoIndex);
348 InputStream contentStream = authenticatedData.getInputStream();
349 ByteArrayOutputStream os = new ByteArrayOutputStream();
350 Util.copyStream(contentStream, os, null);
351
352 if (authenticatedData.verifyMac() == false) {
353 throw new CMSException("Mac verification error!");
354 }
355 System.out.println("Mac successfully verified!");
356
357 return os.toByteArray();
358
359 } catch (InvalidKeyException ex) {
360 throw new CMSException("Key error: "+ex.getMessage());
361 } catch (NoSuchAlgorithmException ex) {
362 throw new CMSException(ex.toString());
363 }
364 }
365
366 /**
367 * Decrypts the encrypted MAC key for the recipient identified by recipient identifier
368 * and uses the MAC key to verify the authenticated data.
369 * <p>
370 * This way of decrypting the encrypted mac key may be used for any type of RecipientInfo
371 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The
372 * recipient in mind is identified by its recipient identifier.
373 *
374 * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array
375 * @param key the key to decrypt the encrypted mac key
376 * @param recipientID the recipient identifier uniquely identifying the key of the
377 * recipient
378 *
379 * @return the verified message, as byte array
380 *
381 * @throws CMSException if the authenticated data cannot be verified
382 * @throws IOException if a stream read/write error occurs
383 */
384 public byte[] getAuthenticatedDataStream(byte[] encoding,
385 byte[] message,
386 Key key,
387 KeyIdentifier recipientID)
388 throws CMSException, IOException {
389
390 // parse the BER encoded AuthenticatedData
391 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
392 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
393
394 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
395 // in explicit mode explicitly supply the content for hash/mac computation
396 authenticatedData.setInputStream(new ByteArrayInputStream(message));
397 }
398
399 // get the right RecipientInfo
400 System.out.println("\nSearch for RecipientInfo:");
401 RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID);
402 if (recipient != null) {
403 System.out.println("RecipientInfo: " + recipient);
404 } else {
405 throw new CMSException("No recipient with ID: " + recipientID);
406 }
407 // decrypt the mac key and verify the content mac
408 try {
409 System.out.println("Decrypt encrypted mac key...");
410 SecretKey cek = recipient.decryptKey(key, recipientID);
411 System.out.println("Verify content mac with decrypted mac key...");
412 authenticatedData.setupMac(cek);
413 InputStream contentStream = authenticatedData.getInputStream();
414 ByteArrayOutputStream os = new ByteArrayOutputStream();
415 Util.copyStream(contentStream, os, null);
416
417 if (authenticatedData.verifyMac() == false) {
418 throw new CMSException("Mac verification error!");
419 }
420 System.out.println("Mac successfully verified!");
421
422 return os.toByteArray();
423
424 } catch (InvalidKeyException ex) {
425 throw new CMSException("Key error: "+ex.getMessage());
426 } catch (NoSuchAlgorithmException ex) {
427 throw new CMSException(ex.toString());
428 }
429 }
430
431 /**
432 * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for
433 * the recipient identified by its recipient certificate or kekID.
434 * <p>
435 *
436 * @param encoding the <code>AuthenticatedData</code> object as DER encoded byte array
437 * @param key the key to decrypt the message
438 * @param recipientCert the certificate of the recipient having a RecipientInfo of
439 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo
440 * @param kekID the kekID identifying the recipient key when using a RecipientInfo
441 * of type KEKRecipientInfo
442 *
443 * @return the verified message, as byte array
444 *
445 * @throws CMSException if the authenticated data cannot be verified
446 * @throws IOException if a stream read/write error occurs
447 */
448 public byte[] getAuthenticatedDataStream(byte[] encoding,
449 byte[] message,
450 Key key,
451 X509Certificate recipientCert,
452 byte[] kekID)
453 throws CMSException, IOException {
454
455 // create the AuthenticatedData object from a DER encoded byte array
456 // we are testing the stream interface
457 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
458 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is);
459
460 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
461 // in explicit mode explicitly supply the content for hash/mac computation
462 authenticatedData.setInputStream(new ByteArrayInputStream(message));
463 }
464
465
466 // decrypt the mac key and verify the content mac
467 try {
468 System.out.println("Verify mac...");
469 if (recipientCert != null) {
470 authenticatedData.setupMac(key, recipientCert);
471 } else {
472 // KEKRecipientInfo
473 authenticatedData.setupMac(key, new KEKIdentifier(kekID));
474 }
475 InputStream contentStream = authenticatedData.getInputStream();
476 ByteArrayOutputStream os = new ByteArrayOutputStream();
477 Util.copyStream(contentStream, os, null);
478
479 if (authenticatedData.verifyMac() == false) {
480 throw new CMSException("Mac verification error!");
481 }
482 System.out.println("Mac successfully verified!");
483
484 return os.toByteArray();
485
486 } catch (InvalidKeyException ex) {
487 throw new CMSException("Key error: "+ex.getMessage());
488 } catch (NoSuchAlgorithmException ex) {
489 throw new CMSException(ex.toString());
490 }
491 }
492
493
494 /**
495 * Creates the RecipientInfos.
496 *
497 * @return the RecipientInfos created
498 *
499 * @throws CMSException if an error occurs when creating the recipient infos
500 */
501 public RecipientInfo[] createRecipients() throws CMSException {
502
503
504 RecipientInfo[] recipients = new RecipientInfo[4];
505 try {
506
507 // rsaUser1 is the first receiver (cert identified by IssuerAndSerialNumber)
508 recipients[0] = new KeyTransRecipientInfo(rsaUser1,
509 (AlgorithmID)AlgorithmID.rsaEncryption.clone());
510 // rsaUser2 is the second receiver (cert identifief by SubjectKeyIdentifier)
511 recipients[1] = new KeyTransRecipientInfo(rsaUser2,
512 CertificateIdentifier.SUBJECT_KEY_IDENTIFIER,
513 (AlgorithmID)AlgorithmID.rsaEncryption.clone());
514
515 // next recipients use key agreement (Static-Static Diffie-Hellman)
516 // the key encryption (key agreement) algorithm to use:
517 AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ssdhKeyAgreement.clone();
518 // the key wrap algorithm to use:
519 AlgorithmID keyWrapAlg = (AlgorithmID)CMSAlgorithmID.cms_HMACwithAES_wrap.clone();
520 // the length of the key encryption key to be generated:
521 int kekLength = 128;
522 // in static-static mode we may supply user keying material
523 byte[] ukm = new byte[64];
524 random.nextBytes(ukm);
525 // ssdhUser1 is originator
526 recipients[2] = new KeyAgreeRecipientInfo(ssdhUser1,
527 ssdhUser1_pk,
528 KeyIdentifier.ISSUER_AND_SERIALNUMBER,
529 keyEA,
530 keyWrapAlg,
531 kekLength,
532 ukm);
533 // add ssdhUser1 (originator) as recipient, too
534 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser1, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
535 // ssdhUser2 is the recipient (cert identified by RecipientKeyIdentifier)
536 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser2, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
537
538 // last receiver uses a symmetric key encryption key
539 AlgorithmID kea = (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone();
540 KEKIdentifier kekIdentifier = new KEKIdentifier(kekID);
541 recipients[3] = new KEKRecipientInfo(kekIdentifier, kea, kek);
542
543 } catch (Exception ex) {
544 throw new CMSException("Error adding recipients: " + ex.getMessage());
545 }
546 return recipients;
547 }
548
549 /**
550 * Parses an AuthenticatedData, decrypts the mac keys for all test recipients
551 * using the index into the recipientInfos field for identifying the recipient
552 * and verifies the content mac.
553 *
554 * @param encodedAuthenticatedData the encoded AuthenticatedData object
555 *
556 * @throws Exception if some error occurs during mac key decryption / mac verification
557 */
558 public void parseAuthenticatedDataWithRecipientInfoIndex(byte[] encodedAuthenticatedData,
559 byte[] message)
560 throws Exception {
561
562 byte[] received_message;
563
564 // rsaUser1
565 System.out.println("\nVerify MAC for rsaUser1:");
566
567 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
568 message,
569 rsaUser1_pk,
570 0);
571 System.out.print("\nContent: ");
572 System.out.println(new String(received_message));
573
574 // rsaUser2
575 System.out.println("\nVerify MAC for rsaUser2:");
576 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
577 message,
578 rsaUser2_pk,
579 1);
580
581 // ssdhUser1
582 System.out.println("\nVerify MAC for ssdhUser1:");
583
584 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
585 message,
586 ssdhUser1_pk,
587 2);
588 System.out.print("\nContent: ");
589 System.out.println(new String(received_message));
590
591 // ssdhUser2
592 System.out.println("\nVerify MAC for ssdhUser2:");
593 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
594 message,
595 ssdhUser2_pk,
596 2);
597
598 // kekUser
599 System.out.println("\nVerify MAC for kekUser:");
600 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
601 message,
602 kek,
603 3);
604 System.out.print("\nContent: ");
605 System.out.println(new String(received_message));
606 }
607
608 /**
609 * Parses an AuthenticatedData, decrypts the mac keys for all test recipients
610 * using their recipient identifiers for identifying the recipient
611 * and verifies the content mac.
612 *
613 * @param encodedAuthenticatedData the encoded AuthenticatedData object
614 *
615 * @throws Exception if some error occurs during mac key decryption / mac verification
616 */
617 public void parseAuthenticatedDataWithRecipientIdentifier(byte[] encodedAuthenticatedData,
618 byte[] message)
619 throws Exception {
620
621 byte[] received_message;
622
623 // rsaUser1
624 System.out.println("\nVerify MAC for rsaUser1:");
625 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
626 message,
627 rsaUser1_pk,
628 new IssuerAndSerialNumber(rsaUser1));
629 System.out.print("\nContent: ");
630 System.out.println(new String(received_message));
631
632 // rsaUser2
633 System.out.println("\nVerify MAC for rsaUser2:");
634 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
635 message,
636 rsaUser2_pk,
637 new SubjectKeyID(rsaUser2));
638
639 // ssdhUser1
640 System.out.println("\nVerify MAC for ssdhUser1:");
641 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
642 message,
643 ssdhUser1_pk,
644 new IssuerAndSerialNumber(ssdhUser1));
645 System.out.print("\nContent: ");
646 System.out.println(new String(received_message));
647
648 // ssdhUser2
649 System.out.println("\nVerify MAC for ssdhUser2:");
650 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
651 message,
652 ssdhUser2_pk,
653 new RecipientKeyIdentifier(ssdhUser2));
654
655 // kekUser
656 System.out.println("\nVerify MAC for kekUser:");
657 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
658 message,
659 kek,
660 new KEKIdentifier(kekID));
661
662 System.out.print("\nDecrypted content: ");
663 System.out.println(new String(received_message));
664
665 System.out.print("\nContent: ");
666 System.out.println(new String(received_message));
667 }
668
669 /**
670 * Parses an AuthenticatedData, decrypts the encrypted mac keys for all test recipients
671 * using their recipient certificate (or KEK id) for identifying the recipient
672 * and verifies the content mac.
673 *
674 * @param encodedAuthenticatedData the encoded AuthenticatedData object
675 *
676 * @throws Exception if some error occurs during mac key decryption / mac verification
677 */
678 public void parseAuthenticatedDataWithRecipientCertOrKEKId(byte[] encodedAuthenticatedData,
679 byte[] message)
680 throws Exception {
681
682 byte[] received_message;
683 // rsaUser1
684 System.out.println("\nVerify MAC for rsaUser1:");
685 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
686 message,
687 rsaUser1_pk,
688 rsaUser1,
689 null);
690 System.out.print("\nContent: ");
691 System.out.println(new String(received_message));
692 // rsaUser2
693 System.out.println("\nVerify MAC for rsaUser2:");
694 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
695 message,
696 rsaUser2_pk,
697 rsaUser2,
698 null);
699
700
701 // ssdhUser1
702 System.out.println("\nVerify MAC for ssdhUser1:");
703 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
704 message,
705 ssdhUser1_pk,
706 ssdhUser1,
707 null);
708 System.out.print("\nContent: ");
709 System.out.println(new String(received_message));
710 // ssdhUser2
711 System.out.println("\nVerify MAC for ssdhUser2:");
712 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
713 message,
714 ssdhUser2_pk,
715 ssdhUser2,
716 null);
717 // kekUser
718 System.out.println("\nVerify MAC for kekUser:");
719 received_message = getAuthenticatedDataStream(encodedAuthenticatedData,
720 message,
721 kek,
722 null,
723 kekID);
724 System.out.print("\nDecrypted content: ");
725 System.out.println(new String(received_message));
726
727 System.out.print("\nContent: ");
728 System.out.println(new String(received_message));
729 }
730
731 /**
732 * Starts the test.
733 */
734 public void start() {
735 // the test message
736 String m = "This is the test message.";
737 System.out.println("Test message: \""+m+"\"");
738 System.out.println();
739 byte[] message = m.getBytes();
740
741 try {
742 byte[] encodedAuthenticatedData;
743
744 AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
745 int macKeyLength = 32;
746 AlgorithmID digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
747
748 System.out.println("Stream implementation demos");
749 System.out.println("===========================");
750
751 // implict mode; with authenticated attributes
752 System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, implicit mode]:\n");
753 encodedAuthenticatedData = createAuthenticatedDataStream(message,
754 macAlgorithm,
755 macKeyLength,
756 digestAlgorithm,
757 AuthenticatedDataOutputStream.IMPLICIT);
758 // transmit data
759 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
760 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
761 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null);
762 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
763 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null);
764 System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
765 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null);
766
767 // implict mode; without authenticated attributes
768 System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n");
769 encodedAuthenticatedData = createAuthenticatedDataStream(message,
770 macAlgorithm,
771 macKeyLength,
772 null,
773 AuthenticatedDataOutputStream.IMPLICIT);
774 // transmit data
775 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n");
776 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
777 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null);
778 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
779 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null);
780 System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
781 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null);
782
783
784 // explict mode; with authenticated attributes
785 System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, explicit mode]:\n");
786 encodedAuthenticatedData = createAuthenticatedDataStream(message,
787 macAlgorithm,
788 macKeyLength,
789 digestAlgorithm,
790 AuthenticatedDataOutputStream.EXPLICIT);
791 // transmit data
792 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n");
793 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
794 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message);
795 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
796 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message);
797 System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
798 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message);
799
800 // explict mode; without authenticated attributes
801 System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, explicit mode]:\n");
802 encodedAuthenticatedData = createAuthenticatedDataStream(message,
803 macAlgorithm,
804 macKeyLength,
805 null,
806 AuthenticatedDataOutputStream.EXPLICIT);
807 // transmit data
808 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n");
809 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field.");
810 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message);
811 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier.");
812 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message);
813 System.out.println("Decrypt and verify for the several recipients using their certificate or kek.");
814 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message);
815
816 } catch (Exception ex) {
817 ex.printStackTrace();
818 throw new RuntimeException(ex.toString());
819 }
820 }
821
822 /**
823 * Main method.
824 *
825 * @throws IOException
826 * if an I/O error occurs when reading required keys
827 * and certificates from files
828 */
829 public static void main(String argv[]) throws Exception {
830
831 DemoUtil.initDemos();
832
833 (new AuthenticatedDataOutputStreamDemo()).start();
834 System.out.println("Ready!");
835 System.in.read();
836 }
837 }