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