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/basic/CMSDemo.java 42    12.02.25 17:58 Dbratko $
029// $Revision: 42 $
030//
031
032package demo.cms.basic;
033
034import java.io.ByteArrayInputStream;
035import java.io.ByteArrayOutputStream;
036import java.io.IOException;
037import java.io.InputStream;
038import java.security.InvalidAlgorithmParameterException;
039import java.security.InvalidKeyException;
040import java.security.NoSuchAlgorithmException;
041import java.security.PrivateKey;
042import java.security.SignatureException;
043import java.security.spec.InvalidParameterSpecException;
044
045import demo.DemoUtil;
046import demo.keystore.CMSKeyStore;
047import iaik.asn1.ObjectID;
048import iaik.asn1.structures.AlgorithmID;
049import iaik.asn1.structures.Attribute;
050import iaik.cms.AuthenticatedData;
051import iaik.cms.AuthenticatedDataStream;
052import iaik.cms.CMSException;
053import iaik.cms.ContentInfo;
054import iaik.cms.ContentInfoStream;
055import iaik.cms.Data;
056import iaik.cms.DataStream;
057import iaik.cms.DigestedData;
058import iaik.cms.DigestedDataStream;
059import iaik.cms.EncryptedContentInfo;
060import iaik.cms.EncryptedContentInfoStream;
061import iaik.cms.EncryptedData;
062import iaik.cms.EncryptedDataStream;
063import iaik.cms.EnvelopedData;
064import iaik.cms.EnvelopedDataStream;
065import iaik.cms.IssuerAndSerialNumber;
066import iaik.cms.KeyTransRecipientInfo;
067import iaik.cms.RecipientInfo;
068import iaik.cms.SignedData;
069import iaik.cms.SignedDataStream;
070import iaik.cms.SignerInfo;
071import iaik.cms.attributes.CMSContentType;
072import iaik.cms.attributes.SigningTime;
073import iaik.utils.Util;
074import iaik.x509.X509Certificate;
075
076/**
077 * This class shows some CMS examples where the content types are
078 * wrapped into a ContentInfo.
079 * <p>
080 * All keys and certificates are read from a keystore created by the
081 * SetupCMSKeyStore program.
082 * <p>
083 * This class demonstrates how to wrap the several CMS types into ContentInfos:
084 * <p><ul>
085 * <li>Data
086 * <li>AuthenticatedData
087 * <li>EncryptedData for PBE encrypting the content
088 * <li>EnvelopedData
089 * <li>DigestedData including the message
090 * <li>DigestedData without message
091 * <li>SignedData including the message
092 * <li>SignedData without message
093 * </ul><p>
094 * Additionally, a <i>SignedAndEncryptedData</i> test is performed, which
095 * is a sequential combination of signed and enveloped data content types.
096 * <p>
097 * All sub-tests use the same proceeding: A test message is properly
098 * processed to give the requested content type object, which subsequently
099 * is encoded to be "sent" to some recipient, who parses it for the
100 * inherent structures.
101 */
102public class CMSDemo {
103
104  // signing certificate of user 1
105  X509Certificate user1_sign;
106  // signing private key of user 1
107  PrivateKey user1_sign_pk;
108  // signing certificate of user 2
109  X509Certificate user2_sign;
110  // signing private key of user 2
111  PrivateKey user2_sign_pk;
112  
113  // encryption certificate of user 1
114  X509Certificate user1_crypt;
115  // encryption private key of user 1
116  PrivateKey user1_crypt_pk;
117  // encryption certificate of user 2
118  X509Certificate user2_crypt;
119  // encryption private key of user 2
120  PrivateKey user2_crypt_pk;
121  // a certificate chain containing the user certs + CA
122  
123  X509Certificate[] certificates;
124
125
126  /**
127   * Setup the demo certificate chains.
128   *
129   * Keys and certificate are retrieved from the demo KeyStore.
130   *
131   * @throws IOException if an file read error occurs
132   */
133  public CMSDemo() throws IOException {
134    
135    System.out.println();
136    System.out.println("***************************************************************************************");
137    System.out.println("*                                 Basic CMS Demo                                      *");
138    System.out.println("*        (shows the usage of the several CMS content type implementations)            *");
139    System.out.println("***************************************************************************************");
140    System.out.println();
141    
142    
143    // signing certs
144    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
145    user1_sign = certs[0];
146    user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
147    user2_sign = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1)[0];
148    user2_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
149    certificates = new X509Certificate[certs.length+1];
150    System.arraycopy(certs, 0, certificates, 0, certs.length);
151    certificates[certs.length] = user2_sign;
152    
153    // encryption certs
154    user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
155    user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
156    user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
157    user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
158
159  }
160
161  /**
162   * Creates a CMS <code>Data</code> object and wraps it into a ContentInfo.
163   * <p>
164   * @param message the message to be sent, as byte representation
165   * @return the encoded ContentInfo containing the <code>Data</code> object just created
166   * @throws CMSException if the <code>Data</code> object cannot
167   *                          be created
168   * @throws IOException if an I/O error occurs
169   */
170  public byte[] createDataStream(byte[] message) throws CMSException, IOException  {
171
172    System.out.println("Create a new Data message:");
173
174    // we are testing the stream interface
175    ByteArrayInputStream is = new ByteArrayInputStream(message);
176
177    // create a new Data object which includes the data
178    DataStream data = new DataStream(is, 2048);
179
180    ContentInfoStream cis = new ContentInfoStream(data);
181    // return the ContentInfo as BER encoded byte array where Data is encoded with block size 2048
182    ByteArrayOutputStream os = new ByteArrayOutputStream();
183    cis.writeTo(os);
184    return os.toByteArray();
185  }
186
187  /**
188   * Parses a CMS <code>Data</code> object.
189   *
190   * @param data the encoded ContentInfo holding the <code>Data</code>
191   *
192   * @return the inherent message as byte array
193   * @throws CMSException if an parsing exception occurs
194   * @throws IOException if an I/O error occurs
195   */
196  public byte[] getDataStream(byte[] data) throws CMSException, IOException {
197
198    // we are testing the stream interface
199    ByteArrayInputStream is = new ByteArrayInputStream(data);
200    ContentInfoStream cis = new ContentInfoStream(is);
201    System.out.println("This ContentInfo holds content of type " + cis.getContentType().getName());
202    // create the Data object
203    DataStream dataStream = (DataStream)cis.getContent();
204
205    // get an InputStream for reading the signed content
206    InputStream content = dataStream.getInputStream();
207    ByteArrayOutputStream os = new ByteArrayOutputStream();
208    Util.copyStream(content, os, null);
209
210    return os.toByteArray();
211  }
212
213  /**
214   * Creates a CMS <code>EnvelopedData</code> and wraps it into a ContentInfo.
215   *
216   * @param message the message to be enveloped, as byte representation
217   * @return the encoded ContentInfo containing the EnvelopedData object just created
218   *
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[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException {
224
225    EnvelopedDataStream enveloped_data;
226
227    // we are testing the stream interface
228    ByteArrayInputStream is = new ByteArrayInputStream(message);
229    // create a new EnvelopedData object encrypted with TripleDES CBC
230    try {
231      enveloped_data = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
232    } catch (NoSuchAlgorithmException ex) {
233      throw new CMSException(ex.toString());
234    }
235
236    // create the recipient infos
237    RecipientInfo[] recipients = new RecipientInfo[2];
238    // user1 is the first receiver
239    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
240    // user2 is the second receiver
241    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
242
243    // specify the recipients of the encrypted message
244    enveloped_data.setRecipientInfos(recipients);
245
246    // return the EnvelopedDate as DER encoded byte array with block size 2048
247    ByteArrayOutputStream os = new ByteArrayOutputStream();
248    enveloped_data.setBlockSize(2048);
249    ContentInfoStream cis = new ContentInfoStream(enveloped_data);
250    cis.writeTo(os);
251    return os.toByteArray();
252  }
253
254  /**
255   * Decrypts the encrypted content of the given EnvelopedData object for the
256   * specified recipient and returns the decrypted (= original) message.
257   *
258   * @param encoding the encoded ContentInfo containing an EnvelopedData object
259   * @param privateKey the private key to decrypt the message
260   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
261   *                           to which the specified private key belongs
262   *
263   * @return the recovered message, as byte array
264   * @throws CMSException if the message cannot be recovered
265   * @throws IOException if an I/O error occurs
266   */
267  public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws CMSException, IOException {
268
269    // create the EnvelopedData object from a DER encoded byte array
270    // we are testing the stream interface
271    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
272    ContentInfoStream cis = new ContentInfoStream(is);
273    EnvelopedDataStream enveloped_data = (EnvelopedDataStream)cis.getContent();
274
275    System.out.println("Information about the encrypted data:");
276    EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
277    System.out.println("Content type: "+eci.getContentType().getName());
278    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
279
280    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
281    RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
282    for (int i=0; i<recipients.length; i++) {
283      System.out.println("Recipient "+(i+1)+":");
284      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
285    }
286
287    // decrypt the message
288    try {
289      enveloped_data.setupCipher(privateKey, recipientInfoIndex);
290      InputStream decrypted = enveloped_data.getInputStream();
291      ByteArrayOutputStream os = new ByteArrayOutputStream();
292      Util.copyStream(decrypted, os, null);
293
294      return os.toByteArray();
295
296    } catch (InvalidKeyException ex) {
297      throw new CMSException("Private key error: "+ex.toString());
298    } catch (NoSuchAlgorithmException ex) {
299      throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
300    }
301  }
302
303  /**
304   * Creates a CMS <code>SignedData</code> object ans wraps it into a ContentInfo.
305   * <p>
306   *
307   * @param message the message to be signed, as byte representation
308   * @param mode the mode indicating whether to include the content 
309   *        (SignedDataStream.IMPLICIT) or not (SignedDataStream.EXPLICIT)
310   * @return the encoding of the ContentInfo holding the <code>SignedData</code> object just created
311   * @throws CMSException if the <code>SignedData</code> object cannot
312   *                          be created
313   * @throws IOException if an I/O error occurs
314   */
315  public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException  {
316
317    System.out.println("Create a new message signed by user 1:");
318
319    // we are testing the stream interface
320    ByteArrayInputStream is = new ByteArrayInputStream(message);
321    // create a new SignedData object which includes the data
322    SignedDataStream signed_data = new SignedDataStream(is, mode);
323    // SignedData shall include the certificate chain for verifying
324    signed_data.setCertificates(certificates);
325
326    // cert at index 0 is the user certificate
327    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
328
329    // create a new SignerInfo
330    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), AlgorithmID.sha256WithRSAEncryption, user1_sign_pk);
331    
332    // create some signed attributes
333    // the message digest attribute is automatically added
334    Attribute[] attributes = new Attribute[2];
335    try {
336      // content type is data
337      CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
338      attributes[0] = new Attribute(contentType);
339      // signing time is now
340      SigningTime signingTime = new SigningTime();
341      attributes[1] = new Attribute(signingTime);
342    } catch (Exception ex) {
343      throw new CMSException("Error creating attribute: " + ex.toString());   
344    }    
345    
346    // set the attributes
347    signer_info.setSignedAttributes(attributes);
348    // finish the creation of SignerInfo by calling method addSigner
349    try {
350      signed_data.addSignerInfo(signer_info);
351
352      // another SignerInfo without authenticated attributes 
353      signer_info = new SignerInfo(new IssuerAndSerialNumber(user2_sign),
354          (AlgorithmID)AlgorithmID.sha256.clone(), user2_sign_pk);
355      // the message digest itself is protected
356      signed_data.addSignerInfo(signer_info);
357
358    } catch (NoSuchAlgorithmException ex) {
359      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
360    }
361    // ensure block encoding
362    signed_data.setBlockSize(2048);
363
364    // write the data through SignedData to any out-of-band place
365    if (mode == SignedDataStream.EXPLICIT) {
366      InputStream data_is = signed_data.getInputStream();
367      byte[] buf = new byte[1024];
368      int r;
369      while ((r = data_is.read(buf)) > 0) {
370        ;   // skip data
371      }   
372    }
373
374    // create the ContentInfo
375    ContentInfoStream cis = new ContentInfoStream(signed_data);
376    // return the SignedData as encoded byte array with block size 2048
377    ByteArrayOutputStream os = new ByteArrayOutputStream();
378
379    cis.writeTo(os);
380    return os.toByteArray();
381  }
382
383  /**
384   * Parses a CMS <code>SignedData</code> object and verifies the signatures
385   * for all participated signers.
386   *
387   * @param signedData the ContentInfo with inherent SignedData, as BER encoded byte array
388   * @param message the the message which was transmitted out-of-band (explicit signed)
389   *
390   * @return the inherent message as byte array, or <code>null</code> if there
391   *         is no message included into the supplied <code>SignedData</code>
392   *         object
393   * @throws CMSException if any signature does not verify
394   * @throws IOException if an I/O error occurs
395   */
396  public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException {
397
398    // we are testing the stream interface
399    ByteArrayInputStream is = new ByteArrayInputStream(signedData);
400    // create the ContentInfo object
401    SignedDataStream signed_data = new SignedDataStream(is);
402
403    if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
404      // explicitly signed; set the content received by other means
405      signed_data.setInputStream(new ByteArrayInputStream(message));
406    }
407    
408    // get an InputStream for reading the signed content
409    InputStream data = signed_data.getInputStream();
410    ByteArrayOutputStream os = new ByteArrayOutputStream();
411    Util.copyStream(data, os, null);
412
413    System.out.println("SignedData contains the following signer information:");
414    SignerInfo[] signer_infos = signed_data.getSignerInfos();
415    int numberOfSignerInfos = signer_infos.length;
416    if (numberOfSignerInfos == 0) {
417      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
418      System.err.println(warning);
419      throw new CMSException(warning);
420    } else {
421      for (int i = 0; i < numberOfSignerInfos; i++) {
422        try {
423          // verify the signed data using the SignerInfo at index i
424          X509Certificate signer_cert = signed_data.verify(i);
425          // if the signature is OK the certificate of the signer is returned
426          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
427          // get signed attributes
428          SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
429          if (signingTime != null) {
430            System.out.println("This message has been signed at " + signingTime.get());
431          } 
432          CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
433          if (contentType != null) {
434            System.out.println("The content has CMS content type " + contentType.get().getName());
435          }
436  
437        } catch (SignatureException ex) {
438          // if the signature is not OK a SignatureException is thrown
439          System.out.println("Signature ERROR from signer: "+signed_data.getCertificate((signer_infos[i].getSignerIdentifier())).getSubjectDN());
440          throw new CMSException(ex.toString());
441        } 
442      }
443      // now check alternative signature verification
444      System.out.println("Now check the signature assuming that no certs have been included:");
445      try {
446         SignerInfo signer_info = signed_data.verify(user1_sign);
447          // if the signature is OK the certificate of the signer is returned
448          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
449  
450      } catch (SignatureException ex) {
451          // if the signature is not OK a SignatureException is thrown
452          System.out.println("Signature ERROR from signer: "+user1_sign.getSubjectDN());
453          throw new CMSException(ex.toString());
454      }
455  
456      try {
457         SignerInfo signer_info = signed_data.verify(user2_sign);
458          // if the signature is OK the certificate of the signer is returned
459          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
460  
461      } catch (SignatureException ex) {
462          // if the signature is not OK a SignatureException is thrown
463          System.out.println("Signature ERROR from signer: "+user2_sign.getSubjectDN());
464          throw new CMSException(ex.toString());
465      }
466      
467      // in practice we also would validate the signer certificate(s)
468    }
469
470    return os.toByteArray();
471  }
472
473  
474  /**
475   * Creates a <i>SignedAndEncrypted</i> (i.e. sequential combination of <code>
476   * SignedData</code> and <code>EnvelopedData</code>). 
477   *
478   * @param message the message to be signed and encrypted, as byte representation
479   * @return the encoded ContentInfo holding the signed and encrypted message object
480   *         just created
481   * @throws CMSException if the the <code>SignedData</code> or
482   *                          <code>EnvelopedData</code> object cannot be created
483   * @throws IOException if an I/O error occurs
484   */
485  public byte[] createSignedAndEncryptedDataStream(byte[] message) throws CMSException, IOException {
486
487    System.out.println("Create a new message signed by user1 encrypted for user2:");
488
489    byte[] signed = createSignedDataStream(message, SignedData.IMPLICIT);
490    return createEnvelopedDataStream(signed);
491  }
492
493  /**
494   * Recovers the original message and verifies the signature.
495   *
496   * @param in the encoded CMS object
497   * @return the recovered message, as byte array
498   * @throws CMSException if the message cannot be recovered
499   * @throws IOException if an I/O error occurs
500   */
501  public byte[] getSignedAndEncryptedDataStream(byte[] in) throws CMSException, IOException {
502
503    // user2 means index 2 (hardcoded for this demo)
504    byte[] signed = getEnvelopedDataStream(in, user2_crypt_pk, 1);
505    return getSignedDataStream(signed, null);
506  }
507
508
509   /**
510   * Creates a CMS <code>DigestedData</code> object.
511   * <p>
512   * @param message the message to be digested, as byte representation
513   * @return the encoded ContentInfo containing the DigestedData object just created
514   * @throws CMSException if the <code>DigestedData</code> object cannot
515   *                          be created
516   * @throws IOException if an I/O error occurs
517   */
518  public byte[] createDigestedDataStream(byte[] message, int mode) throws CMSException, IOException  {
519
520    System.out.println("Create a new message to be digested:");
521
522    // we are testing the stream interface
523    ByteArrayInputStream is = new ByteArrayInputStream(message);
524
525    // create a new DigestedData object which includes the data
526    DigestedDataStream digested_data = null;
527
528    digested_data = new DigestedDataStream(is, (AlgorithmID)AlgorithmID.sha256.clone(), mode);
529    digested_data.setBlockSize(2048);
530
531    // write the data through DigestedData to any out-of-band place
532    if (mode == DigestedDataStream.EXPLICIT) {
533      InputStream data_is = digested_data.getInputStream();
534      byte[] buf = new byte[1024];
535      int r;
536      while ((r = data_is.read(buf)) > 0) {
537        ;   // skip data
538      }
539    }
540
541    // wrap into ContentInfo and encode
542    ByteArrayOutputStream os = new ByteArrayOutputStream();
543    ContentInfoStream cis = new ContentInfoStream(digested_data);
544    cis.writeTo(os);
545    return os.toByteArray();
546  }
547
548  /**
549   * Parses a CMS <code>DigestedData</code> object and verifies the hash.
550   *
551   * @param digestedData the encoded ContentInfo holding a DigestedData object
552   * @param message the the message which was transmitted out-of-band
553   *
554   * @return the inherent message as byte array, or <code>null</code> if there
555   *         is no message included into the supplied <code>DigestedData</code>
556   *         object
557   * @throws CMSException if any signature does not verify
558   * @throws IOException if an I/O error occurs
559   */
560  public byte[] getDigestedDataStream(byte[] digestedData, byte[] message) throws CMSException, IOException {
561
562    // we are testing the stream interface
563    ByteArrayInputStream is = new ByteArrayInputStream(digestedData);
564    // create the DigestedData object
565    DigestedDataStream digested_data = new DigestedDataStream(is);
566    if (digested_data.getMode() == DigestedDataStream.EXPLICIT) {
567      digested_data.setInputStream(new ByteArrayInputStream(message));
568    }
569
570    // get an InputStream for reading the signed content
571    InputStream data = digested_data.getInputStream();
572    ByteArrayOutputStream os = new ByteArrayOutputStream();
573    Util.copyStream(data, os, null);
574
575    if (digested_data.verify()) {
576      System.out.println("Hash ok!");
577    } else {
578      throw new CMSException("Hash verification failed!");
579    }
580
581    return os.toByteArray();
582  }
583
584  /**
585   * Creates a CMS <code>EncryptedDataStream</code> message.
586   * <p>
587   * The supplied content is PBE-encrypted using the specified password.
588   *
589   * @param message the message to be encrypted, as byte representation
590   * @param pbeAlgorithm the PBE algorithm to be used
591   * @param password the password
592   * @return the DER encoding of the ContentInfo holding the <code>EncryptedData</code> object just created
593   * @throws CMSException if the <code>EncryptedData</code> object cannot
594   *                          be created
595   * @throws IOException if an I/O error occurs
596   */
597  public byte[] createEncryptedDataStream(byte[] message, AlgorithmID pbeAlgorithm, char[] password) throws CMSException, IOException {
598
599    EncryptedDataStream encrypted_data;
600
601    // we are testing the stream interface
602    ByteArrayInputStream is = new ByteArrayInputStream(message);
603   
604    try {
605      encrypted_data = new EncryptedDataStream(is, 2048);
606      encrypted_data.setupCipher(pbeAlgorithm, password);
607    } catch (InvalidKeyException ex) {
608      throw new CMSException("Key error: "+ex.toString());
609    } catch (NoSuchAlgorithmException ex) {
610      throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
611    }
612
613    // wrap into ContentInfo and encode
614    ByteArrayOutputStream os = new ByteArrayOutputStream();
615    ContentInfoStream cis = new ContentInfoStream(encrypted_data);
616    cis.writeTo(os);
617    return os.toByteArray();
618  }
619
620  /**
621   * Decrypts the PBE-encrypted content of the given <code>EncryptedData</code> object
622   * using the specified password and returns the decrypted (= original) message.
623   *
624   * @param encoding the encoded ContentInfo holding an <code>EncryptedData</code> object
625   * @param password the password to decrypt the message
626   *
627   * @return the recovered message, as byte array
628   * @throws CMSException if the message cannot be recovered
629   * @throws IOException if an I/O error occurs
630   */
631  public byte[] getEncryptedDataStream(byte[] encoding, char[] password) throws CMSException, IOException {
632
633    // create the EncryptpedData object from a DER encoded byte array
634    // we are testing the stream interface
635    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
636     // create the ContentInfo
637    ContentInfoStream cis = new ContentInfoStream(is);
638
639    EncryptedDataStream encrypted_data = (EncryptedDataStream)cis.getContent();
640
641    System.out.println("Information about the encrypted data:");
642    EncryptedContentInfoStream eci = encrypted_data.getEncryptedContentInfo();
643    System.out.println("Content type: "+eci.getContentType().getName());
644    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
645
646    // decrypt the message
647    try {
648      encrypted_data.setupCipher(password);
649      InputStream decrypted = encrypted_data.getInputStream();
650      ByteArrayOutputStream os = new ByteArrayOutputStream();
651      Util.copyStream(decrypted, os, null);
652
653      return os.toByteArray();
654
655    } catch (InvalidKeyException ex) {
656      throw new CMSException("Key error: "+ex.toString());
657    } catch (NoSuchAlgorithmException ex) {
658      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
659    } catch (InvalidAlgorithmParameterException ex) {
660      throw new CMSException("Invalid Parameters: "+ex.getMessage());
661    } catch (InvalidParameterSpecException ex) {
662      throw new CMSException("Invalid Parameters: "+ex.getMessage());
663    }
664  }
665  
666  /**
667   * Creates a CMS <code>AuthenticatedDataStream</code> for the given message message.
668   * <p>
669   * <b>Attention:</b> This AuthenticatedData demo uses RSA as key management technique.
670   * In practice (see RFC 5652) a key management technique that provides data origin
671   * authentication should be used like, for instance, Static-Static Diffie-Hellman when
672   * both the originator and recipient public keys are bound to appropriate identities 
673   * in X.509 certificates, see, for instance, {@link demo.cms.authenticatedData.AuthenticatedDataDemo
674   * AuthenticatedDataDemo}.
675   *
676   * @param message the message to be authenticated, as byte representation
677   * @param includeAuthAttrs whether to include authenticated attributes
678   * @param mode the mode indicating whether to include the content 
679   *        (AuthenticatedDataStream.IMPLICIT) or not (AuthenticatedDataStream.EXPLICIT)
680   * @return the BER encoding of the <code>AuthenticatedData</code> object, wrapped in a ContentInfo
681   * @throws CMSException if the <code>AuthenticatedData</code> object cannot
682   *                         be created
683   * @throws IOException if an I/O error occurs
684   */
685  public byte[] createAuthenticatedDataStream(byte[] message,
686                                              boolean includeAuthAttrs,
687                                              int mode)
688    throws CMSException, IOException {
689    
690    AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
691    int macKeyLength = 32;
692    AlgorithmID digestAlgorithm = null;
693    // we need a digest algorithm if authenticated attributes shall be included
694    if (includeAuthAttrs == true) {
695      digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
696    }   
697    ObjectID contentType = ObjectID.cms_data;
698    
699    AuthenticatedDataStream authenticatedData;
700
701    // we are testing the stream interface
702    ByteArrayInputStream is = new ByteArrayInputStream(message);
703    // create a new AuthenticatedData object 
704    try {
705      authenticatedData = new AuthenticatedDataStream(contentType,
706                                                      is, 
707                                                      macAlgorithm,
708                                                      macKeyLength,
709                                                      null,
710                                                      digestAlgorithm,
711                                                      mode);
712    } catch (NoSuchAlgorithmException ex) {
713      throw new CMSException(ex.toString());
714    }
715
716    // create the recipient infos
717    RecipientInfo[] recipients = new RecipientInfo[2];
718    // user1 is the first receiver
719    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
720    // user2 is the second receiver
721    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
722    // specify the recipients of the authenticated message
723    authenticatedData.setRecipientInfos(recipients);
724    
725    if (includeAuthAttrs == true) {
726       // create some autheticated attributes
727       // (the message digest attribute is automatically added)
728       try {
729         Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
730         authenticatedData.setAuthenticatedAttributes(attributes);
731       } catch (Exception ex) {
732         throw new CMSException("Error creating attribute: " + ex.toString());   
733       } 
734    }    
735    
736    // in explicit mode get the content and write it  to any out-of-band place
737    if (mode == AuthenticatedDataStream.EXPLICIT) {
738      InputStream data_is = authenticatedData.getInputStream();
739      byte[] buf = new byte[1024];
740      int r;
741      while ((r = data_is.read(buf)) > 0) {
742        ;   // skip data
743      }
744    }    
745    
746    // create the ContentInfo
747    ContentInfoStream cis = new ContentInfoStream(authenticatedData);
748    // return the AuthenticatedData as encoded byte array with block size 2048
749    ByteArrayOutputStream os = new ByteArrayOutputStream();
750
751    cis.writeTo(os);
752    return os.toByteArray();  
753  }
754  
755  /**
756   * Decrypts the encrypted MAC key for the recipient identified by its index
757   * into the recipientInfos field and uses the MAC key to verify
758   * the authenticated data.
759   * <p>
760   * This way of decrypting the MAC key and verifying the content may be used for 
761   * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 
762   * KEKRecipientInfo), but requires to know at what index of the recipientInfos
763   * field the RecipientInfo for the particular recipient in mind can be found. 
764   * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
765   * some processing overhead may take place because a KeyAgreeRecipientInfo may
766   * contain encrypted mac keys for more than only one recipient; since the
767   * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
768   * mac key -- if there are more than only one -- repeated decryption runs may be
769   * required as long as the decryption process completes successfully.
770   * <p>
771   * <b>Attention:</b> This AuthenticatedData demo uses RSA as key management technique.
772   * In practice (see RFC 5652) a key management technique that provides data origin
773   * authentication should be used like, for instance, Static-Static Diffie-Hellman when
774   * both the originator and recipient public keys are bound to appropriate identities 
775   * in X.509 certificates, see, for instance, {@link demo.cms.authenticatedData.AuthenticatedDataDemo
776   * AuthenticatedDataDemo}.
777   *
778   * @param encoding the BER encoded ContentInfo holding the <code>AuthenticatedData</code> object
779   * @param message the content message, if transmitted by other means (explicit mode)
780   * @param key the key to decrypt the mac key 
781   * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
782   *                           which the given key belongs
783   *
784   * @return the verified message, as byte array
785   * @throws CMSException if the authenticated data cannot be verified
786   * @throws IOException if a stream read/write error occurs
787   */
788  public byte[] getAuthenticatedDataStream(byte[] encoding, 
789                                           byte[] message, 
790                                           PrivateKey key, 
791                                           int recipientInfoIndex)
792    throws CMSException, IOException {
793
794    // create the AuthenticatedData object from a DER encoded byte array
795    // we are testing the stream interface
796    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
797    // create the ContentInfo object
798    ContentInfoStream cis = new ContentInfoStream(is);
799    System.out.println("This ContentInfo holds content of type " + cis.getContentType().getName());
800    AuthenticatedDataStream authenticatedData = (AuthenticatedDataStream)cis.getContent();
801    
802    if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) {
803      // in explicit mode explicitly supply the content for hash/mac computation  
804      authenticatedData.setInputStream(new ByteArrayInputStream(message));
805    }
806
807    System.out.println("\nThis message can be verified by the following recipients:");
808    RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
809    for (int i=0; i<recipients.length; i++) {
810      System.out.println("Recipient "+(i+1)+":");
811      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
812    }
813
814    // decrypt the mac key and verify the mac for indented recipient
815    try {
816      authenticatedData.setupMac(key, recipientInfoIndex);
817      InputStream contentStream = authenticatedData.getInputStream();
818      ByteArrayOutputStream os = new ByteArrayOutputStream();
819      Util.copyStream(contentStream, os, null);
820      
821      if (authenticatedData.verifyMac() == false) {
822        throw new CMSException("Mac verification error!");
823      }  
824      System.out.println("Mac successfully verified!");
825      
826      return os.toByteArray();
827
828    } catch (InvalidKeyException ex) {
829      throw new CMSException("Key error: "+ex.getMessage());
830    } catch (NoSuchAlgorithmException ex) {
831      throw new CMSException(ex.toString());
832    }
833  }
834
835
836
837  /**
838   * Creates a CMS <code>Data</code> object.
839   * <p>
840   * @param message the message to be sent, as byte representation
841   * @return the DER encoded ContentInfo holding the <code>Data</code> object just created
842   * @throws CMSException if the <code>Data</code> object cannot
843   *                          be created
844   */
845  public byte[] createData(byte[] message) throws CMSException  {
846
847    System.out.println("Create a new Data message:");
848
849    // create a new DigestedData object which includes the data
850    Data data = new Data(message);
851    ContentInfo ci = new ContentInfo(data);
852    // return the ASN.1 representation
853    return ci.toByteArray();
854  }
855
856  /**
857   * Parses a CMS <code>Data</code> object.
858   *
859   * @param encoding the DER encoded ContentInfo holding with inherent <code>Data</code>
860   *
861   * @return the inherent message as byte array
862   *
863   * @throws CMSException if an parsing exception occurs
864   * @throws IOException if an I/O related error occurs
865   */
866  public byte[] getData(byte[] encoding) throws CMSException, IOException {
867    
868    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
869    // create the ContentInfo
870    ContentInfo ci = new ContentInfo(encodedStream);
871    System.out.println("This ContentInfo holds content of type " + ci.getContentType().getName());
872    // create the Data object
873    Data data = (Data)ci.getContent();
874    
875    // get and return the content
876    return data.getData();
877  }
878
879  /**
880   * Creates a CMS <code>EnvelopedData</code> message and wraps it into a ContentInfo.
881   * <p>
882   *
883   * @param message the message to be enveloped, as byte representation
884   * @return the DER encoded ContentInfo holding the EnvelopedData object just created
885   * @throws CMSException if the <code>EnvelopedData</code> object cannot
886   *                          be created
887   */
888  public byte[] createEnvelopedData(byte[] message) throws CMSException {
889
890    EnvelopedData enveloped_data;
891
892    // create a new EnvelopedData object encrypted with TripleDES CBC
893    try {
894      enveloped_data = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
895    } catch (NoSuchAlgorithmException ex) {
896      throw new CMSException(ex.toString());
897    }
898
899    // create the recipient infos
900    RecipientInfo[] recipients = new RecipientInfo[2];
901    // user1 is the first receiver
902    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
903    // user2 is the second receiver
904    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
905    // specify the recipients of the encrypted message
906    enveloped_data.setRecipientInfos(recipients);
907    
908    // wrap into contentInfo
909    ContentInfo ci = new ContentInfo(enveloped_data);
910    // return the EnvelopedDate as DER encoded byte array
911    return ci.toByteArray();
912  }
913
914  /**
915   * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the
916   * specified recipient and returns the decrypted (= original) message.
917   *
918   * @param encoding the DER encoded ContentInfo holding an EnvelopedData
919   * @param privateKey the private key to decrypt the message
920   * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
921   *                           to which the specified private key belongs
922   *
923   * @return the recovered message, as byte array
924   * @throws CMSException if the message cannot be recovered
925   */
926  public byte[] getEnvelopedData(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws CMSException, IOException {
927    
928    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
929    ContentInfo ci = new ContentInfo(encodedStream);
930    EnvelopedData enveloped_data = (EnvelopedData)ci.getContent();
931
932    System.out.println("Information about the encrypted data:");
933    EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo();
934    System.out.println("Content type: "+eci.getContentType().getName());
935    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
936
937    System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
938    RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
939    for (int i=0; i<recipients.length; i++) {
940      System.out.println("Recipient "+(i+1)+":");
941      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
942    }
943
944    // decrypt the message
945    try {
946      enveloped_data.setupCipher(privateKey, recipientInfoIndex);
947      return enveloped_data.getContent();
948
949    } catch (InvalidKeyException ex) {
950      throw new CMSException("Private key error: "+ex.toString());
951    } catch (NoSuchAlgorithmException ex) {
952      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
953    }
954  }
955
956  /**
957   * Creates a CMS <code>SignedData</code> object and wraps it into a ContentInfo.
958   *
959   * @param message the message to be signed, as byte representation
960   * @param mode the mode indicating whether to include the content 
961   *        (SignedDataStream.IMPLICIT) or not (SignedDataStream.EXPLICIT)
962   * @return the DER encoded ContentInfo holding the <code>SignedData</code> object just created
963   * @throws CMSException if the <code>SignedData</code> object cannot
964   *                          be created
965   */
966  public byte[] createSignedData(byte[] message, int mode) throws CMSException  {
967
968    System.out.println("Create a new message signed by user 1:");
969
970    // create a new SignedData object which includes the data
971    SignedData signed_data = new SignedData(message, mode);
972    // SignedData shall include the certificate chain for verifying
973    signed_data.setCertificates(certificates);
974
975    // cert at index 0 is the user certificate
976    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
977
978    // create a new SignerInfo
979    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
980    
981    // create some signed attributes
982    // the message digest attribute is automatically added
983    Attribute[] attributes = new Attribute[2];
984    try {
985      // content type is data
986      CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
987      attributes[0] = new Attribute(contentType);
988      // signing time is now
989      SigningTime signingTime = new SigningTime();
990      attributes[1] = new Attribute(signingTime);
991    } catch (Exception ex) {
992      throw new CMSException("Error creating attribute: " + ex.toString());   
993    }
994    
995    // set the attributes
996    signer_info.setSignedAttributes(attributes);
997    // finish the creation of SignerInfo by calling method addSigner
998    try {
999      signed_data.addSignerInfo(signer_info);
1000
1001      // another SignerInfo without authenticated attributes 
1002      signer_info = new SignerInfo(new IssuerAndSerialNumber(user2_sign),
1003          (AlgorithmID)AlgorithmID.sha256.clone(), user2_sign_pk);
1004      // the message digest itself is protected
1005      signed_data.addSignerInfo(signer_info);
1006
1007    } catch (NoSuchAlgorithmException ex) {
1008      throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
1009    }
1010
1011    ContentInfo ci = new ContentInfo(signed_data);
1012    return ci.toByteArray();
1013  }
1014
1015  /**
1016   * Parses a CMS <code>SignedData</code> object and verifies the signatures
1017   * for all participated signers.
1018   *
1019   * @param encoding the ContentInfo with inherent <code>SignedData</code> object, as DER encoding
1020   * @param message the the message which was transmitted out-of-band (explicit signed)
1021   *
1022   * @return the inherent message as byte array, or <code>null</code> if there
1023   *         is no message included into the supplied <code>SignedData</code>
1024   *         object
1025   * @throws CMSException if any signature does not verify
1026   * @throws IOException if an I/O error occurs
1027   */
1028  public byte[] getSignedData(byte[] encoding, byte[] message) throws CMSException, IOException {
1029    
1030    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
1031    // create a content info from the ASN.1 object
1032    SignedData signed_data = new SignedData(encodedStream);
1033    
1034    if (signed_data.getMode() == SignedData.EXPLICIT) {
1035      // explicit mode: set content received by other means
1036      signed_data.setContent(message);
1037    }
1038
1039    System.out.println("SignedData contains the following signer information:");
1040    SignerInfo[] signer_infos = signed_data.getSignerInfos();
1041
1042    int numberOfSignerInfos = signer_infos.length;
1043    if (numberOfSignerInfos == 0) {
1044      String warning = "Warning: Unsigned message (no SignerInfo included)!";  
1045      System.err.println(warning);
1046      throw new CMSException(warning);
1047    } else {
1048      for (int i = 0; i < numberOfSignerInfos; i++) {
1049        try {
1050          // verify the signed data using the SignerInfo at index i
1051          X509Certificate signer_cert = signed_data.verify(i);
1052          // if the signature is OK the certificate of the signer is returned
1053          System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
1054          // get signed attributes
1055          SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
1056          if (signingTime != null) {
1057            System.out.println("This message has been signed at " + signingTime.get());
1058          } 
1059          CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
1060          if (contentType != null) {
1061            System.out.println("The content has CMS content type " + contentType.get().getName());
1062          }
1063        } catch (SignatureException ex) {
1064           // if the signature is not OK a SignatureException is thrown
1065           System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
1066           throw new CMSException(ex.toString());
1067        } 
1068      }
1069
1070      // now check alternative signature verification
1071      System.out.println("Now check the signature assuming that no certs have been included:");
1072      try {
1073         SignerInfo signer_info = signed_data.verify(user1_sign);
1074          // if the signature is OK the certificate of the signer is returned
1075          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1076  
1077      } catch (SignatureException ex) {
1078          // if the signature is not OK a SignatureException is thrown
1079          System.out.println("Signature ERROR from signer: "+user1_sign.getSubjectDN());
1080          throw new CMSException(ex.toString());
1081      }
1082  
1083      try {
1084         SignerInfo signer_info = signed_data.verify(user2_sign);
1085          // if the signature is OK the certificate of the signer is returned
1086          System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1087  
1088      } catch (SignatureException ex) {
1089          // if the signature is not OK a SignatureException is thrown
1090          System.out.println("Signature ERROR from signer: "+user2_sign.getSubjectDN());
1091          throw new CMSException(ex.toString());
1092      }
1093      
1094      // in practice we also would validate the signer certificate(s)  
1095    }
1096    return signed_data.getContent();
1097  }
1098
1099
1100  
1101  /**
1102   * Creates a <i>SignedAndEncrypted</i> (i.e. sequential combination of <code>
1103   * SignedData</code> and <code>EnvelopedData</code>) object.
1104   *
1105   * @param message the message to be signed and encrypted, as byte representation
1106   * @return the DER encoded ContentInfo holding the signed and encrypted message object
1107   *         just created
1108   * @throws CMSException if the the <code>SignedData</code> or
1109   *                          <code>EnvelopedData</code> object cannot be created
1110   */
1111  public byte[] createSignedAndEncryptedData(byte[] message) throws CMSException {
1112
1113    System.out.println("Create a new message signed by user1 encrypted for user2:");
1114
1115    byte[] signed = createSignedData(message, SignedData.IMPLICIT);
1116    return createEnvelopedData(signed);
1117  }
1118
1119  /**
1120   * Recovers the original message and verifies the signature.
1121   *
1122   * @param encoding the DER encoded ContentInfo holding a SignedAndEnryptedData object
1123   * @return the recovered message, as byte array
1124   * @throws CMSException if the message cannot be recovered
1125   * @throws IOException if an I/O error occurs
1126   */
1127  public byte[] getSignedAndEncryptedData(byte[] encoding) throws CMSException, IOException {
1128    
1129    // user2 means index 2 (hardcoded for this demo)
1130    byte[] signed = getEnvelopedData(encoding, user2_crypt_pk, 1);
1131    return getSignedData(signed, null);
1132  }
1133
1134
1135  /**
1136   * Creates a CMS <code>DigestedData</code> object.
1137   * <p>
1138   *
1139   * @param message the message to be digested, as byte representation
1140   * @return the <code>DigestedData</code> wrapped into a ContentInfo, as DER encoding
1141   * @throws CMSException if the <code>DigestedData</code> object cannot
1142   *                          be created
1143   */
1144  public byte[] createDigestedData(byte[] message, int mode) throws CMSException  {
1145
1146    System.out.println("Create a new digested message:");
1147
1148    // create a new DigestedData object which includes the data
1149    DigestedData digested_data = new DigestedData(message, (AlgorithmID)AlgorithmID.sha256.clone(), mode);
1150    ContentInfo ci = new ContentInfo(digested_data);
1151    return ci.toByteArray();
1152  }
1153
1154  /**
1155   * Parses a CMS <code>DigestedData</code> object and verifies the hash value.
1156   *
1157   * @param encoding the ContentInfo holding a <code>DigestedData</code>, as DER encoding
1158   * @param message the the message which was transmitted out-of-band (explicit digested)
1159   *
1160   * @return the message
1161   * @throws CMSException if some parsing exception occurs
1162   * @throws IOException if an I/O error occurs
1163   */
1164  public byte[] getDigestedData(byte[] encoding, byte[] message) throws CMSException, IOException {
1165    
1166    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
1167    // create a content info from the ASN.1 object
1168    ContentInfo ci = new ContentInfo(encodedStream);
1169    System.out.println("This ContentInfo holds content of type " + ci.getContentType().getName());
1170
1171    DigestedData digested_data = new DigestedData(encodedStream);
1172
1173    if (digested_data.getMode() == DigestedData.EXPLICIT) {
1174      // set content transmitted by other means
1175      digested_data.setContent(message);
1176    }
1177
1178    // now verify the digest
1179    if (digested_data.verify()) {
1180      System.out.println("Hash ok!");
1181    } else {
1182      throw new CMSException("Hash verification failed!");
1183    }
1184
1185    return digested_data.getContent();
1186  }
1187
1188
1189  /**
1190   * Creates a CMS <code>EncryptedData</code> message.
1191   * <p>
1192   * The supplied content is PBE-encrypted using the specified password.
1193   *
1194   * @param message the message to be encrypted, as byte representation
1195   * @param pbeAlgorithm the PBE algorithm to be used
1196   * @param password the password
1197   * @return the <code>EncryptedData</code> object wrapped into a ContentInfo, as DER encoding
1198   * @throws CMSException if the <code>EncryptedData</code> object cannot
1199   *                          be created
1200   */
1201  public byte[] createEncryptedData(byte[] message, AlgorithmID pbeAlgorithm, char[] password) throws CMSException {
1202
1203    EncryptedData encrypted_data;
1204
1205    try {
1206      encrypted_data = new EncryptedData(message);
1207      // encrypt the message
1208      encrypted_data.setupCipher(pbeAlgorithm, password);
1209    } catch (InvalidKeyException ex) {
1210      throw new CMSException("Key error: "+ex.toString());
1211    } catch (NoSuchAlgorithmException ex) {
1212      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
1213    }
1214    // create the ContentInfo
1215    ContentInfo ci = new ContentInfo(encrypted_data);
1216    return ci.toByteArray();
1217
1218  }
1219
1220  /**
1221   * Decrypts the PBE-encrypted content of the given <code>EncryptedData</code> object
1222   * using the specified password and returns the decrypted (= original) message.
1223   *
1224   * @param encoding the DER encoded ContentInfo holding the <code>EncryptedData</code> object
1225   * @param password the password to decrypt the message
1226   *
1227   * @return the recovered message, as byte array
1228   * @throws CMSException if the message cannot be recovered
1229   * @throws IOException if an I/O error occurs
1230   */
1231  public byte[] getEncryptedData(byte[] encoding, char[] password) throws CMSException, IOException {
1232    
1233    ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
1234    ContentInfo ci = new ContentInfo(encodedStream);
1235    System.out.println("This ContentInfo holds content of type " + ci.getContentType().getName());
1236
1237    // get the EncryptedData
1238    EncryptedData encrypted_data = (EncryptedData)ci.getContent();
1239
1240    System.out.println("Information about the encrypted data:");
1241    EncryptedContentInfo eci = (EncryptedContentInfo)encrypted_data.getEncryptedContentInfo();
1242    System.out.println("Content type: "+eci.getContentType().getName());
1243    System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
1244
1245    // decrypt the message
1246    try {
1247      encrypted_data.setupCipher(password);
1248      return encrypted_data.getContent();
1249
1250    } catch (InvalidKeyException ex) {
1251      throw new CMSException("Key error: "+ex.toString());
1252    } catch (NoSuchAlgorithmException ex) {
1253      throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
1254    } catch (InvalidAlgorithmParameterException ex) {
1255      throw new CMSException("Invalid Parameters: "+ex.toString());
1256    } catch (InvalidParameterSpecException ex) {
1257      throw new CMSException("Invalid Parameters: "+ex.toString());
1258    }
1259  }
1260  
1261  /**
1262   * Creates a CMS <code>AuthenticatedData</code> for the given message message.
1263   * <p>
1264   * <b>Attention:</b> This AuthenticatedData demo uses RSA as key management technique.
1265   * In practice (see RFC 5652) a key management technique that provides data origin
1266   * authentication should be used like, for instance, Static-Static Diffie-Hellman when
1267   * both the originator and recipient public keys are bound to appropriate identities 
1268   * in X.509 certificates, see, for instance, {@link demo.cms.authenticatedData.AuthenticatedDataDemo
1269   * AuthenticatedDataDemo}.
1270   *
1271   * @param message the message to be authenticated, as byte representation
1272   * @param includeAuthAttrs whether to include authenticated attributes
1273   * @param mode the mode indicating whether to include the content 
1274   *             (AuthenticatedData.IMPLICIT) or not (AuthenticatedDatam.EXPLICIT)
1275   * @return the BER encoding of the <code>AuthenticatedData</code> object, wrapped in a ContentInfo
1276   * @throws CMSException if the <code>AuthenticatedData</code> object cannot
1277   *                         be created
1278   * @throws IOException if an I/O error occurs
1279   */
1280  public byte[] createAuthenticatedData(byte[] message,
1281                                        boolean includeAuthAttrs,
1282                                        int mode)
1283    throws CMSException, IOException {
1284    
1285    AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone();
1286    int macKeyLength = 32;
1287    AlgorithmID digestAlgorithm = null;
1288    // we need a digest algorithm if authenticated attributes shall be included
1289    if (includeAuthAttrs == true) {
1290      digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone();
1291    }   
1292    ObjectID contentType = ObjectID.cms_data;
1293    
1294    AuthenticatedData authenticatedData;
1295
1296    // create a new AuthenticatedData object 
1297    try {
1298      authenticatedData = new AuthenticatedData(contentType,
1299                                                message, 
1300                                                macAlgorithm,
1301                                                macKeyLength,
1302                                                null,
1303                                                digestAlgorithm,
1304                                                mode);
1305    } catch (NoSuchAlgorithmException ex) {
1306      throw new CMSException(ex.toString());
1307    }
1308
1309
1310    // create the recipient infos
1311    RecipientInfo[] recipients = new RecipientInfo[2];
1312    // user1 is the first receiver
1313    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
1314    // user2 is the second receiver
1315    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
1316    // specify the recipients of the authenticated message
1317    authenticatedData.setRecipientInfos(recipients);
1318    
1319    if (includeAuthAttrs == true) {
1320      // create some autheticated attributes
1321      // (the message digest attribute is automatically added)
1322      try {
1323        Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) };
1324        authenticatedData.setAuthenticatedAttributes(attributes);
1325      } catch (Exception ex) {
1326        throw new CMSException("Error creating attribute: " + ex.toString());   
1327      } 
1328    }    
1329   
1330    // wrap the AuthenticatedData in a ContentInfo and encode it
1331    ContentInfo ci = new ContentInfo(authenticatedData);
1332    return ci.toByteArray();
1333  
1334  }
1335  
1336  /**
1337   * Decrypts the encrypted MAC key for the recipient identified by its index
1338   * into the recipientInfos field and uses the MAC key to verify
1339   * the authenticated data.
1340   * <p>
1341   * This way of decrypting the MAC key and verifying the content may be used for 
1342   * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 
1343   * KEKRecipientInfo), but requires to know at what index of the recipientInfos
1344   * field the RecipientInfo for the particular recipient in mind can be found. 
1345   * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo
1346   * some processing overhead may take place because a KeyAgreeRecipientInfo may
1347   * contain encrypted mac keys for more than only one recipient; since the
1348   * recipientInfoIndex only specifies the RecipientInfo but not the encrypted
1349   * mac key -- if there are more than only one -- repeated decryption runs may be
1350   * required as long as the decryption process completes successfully.
1351   * <p>
1352   * <b>Attention:</b> This AuthenticatedData demo uses RSA as key management technique.
1353   * In practice (see RFC 5652) a key management technique that provides data origin
1354   * authentication should be used like, for instance, Static-Static Diffie-Hellman when
1355   * both the originator and recipient public keys are bound to appropriate identities 
1356   * in X.509 certificates, see, for instance, {@link demo.cms.authenticatedData.AuthenticatedDataDemo
1357   * AuthenticatedDataDemo}.
1358   *
1359   * @param encoding the DER encoded ContentInfo holding the <code>AuthenticatedData</code> object
1360   * @param message the content message, if transmitted by other means (explicit mode)
1361   * @param key the key to decrypt the mac key
1362   * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 
1363   *                           which the given key belongs
1364   *
1365   * @return the verified message, as byte array
1366   * @throws CMSException if the authenticated data cannot be verified
1367   * @throws IOException if a IO read/write error occurs
1368   */
1369  public byte[] getAuthenticatedData(byte[] encoding, 
1370                                     byte[] message,
1371                                     PrivateKey key,
1372                                     int recipientInfoIndex) 
1373    throws CMSException, IOException {
1374        
1375    // create the AuthenticatedData object from a DER encoded byte array
1376    ByteArrayInputStream is = new ByteArrayInputStream(encoding);
1377    ContentInfo ci = new ContentInfo(is);
1378    System.out.println("This ContentInfo holds content of type " + ci.getContentType().getName());
1379
1380    AuthenticatedData authenticatedData = (AuthenticatedData)ci.getContent();
1381    
1382    if (authenticatedData.getMode() == AuthenticatedData.EXPLICIT) {
1383      // in explicit mode explicitly supply the content for hash/mac computation  
1384      authenticatedData.setContent(message);
1385    }
1386
1387    System.out.println("\nThis message can be verified by the following recipients:");
1388    RecipientInfo[] recipients = authenticatedData.getRecipientInfos();
1389    for (int i=0; i<recipients.length; i++) {
1390      System.out.println("Recipient "+(i+1)+":");
1391      System.out.println(recipients[i].getRecipientIdentifiers()[0]);
1392    }
1393
1394    // decrypt the mac key and verify the mac for the first recipient
1395    try {
1396      authenticatedData.setupMac(key, recipientInfoIndex);
1397      if (authenticatedData.verifyMac() == false) {
1398        throw new CMSException("Mac verification error!");
1399      }  
1400      System.out.println("Mac successfully verified!");
1401      
1402      return authenticatedData.getContent();
1403
1404    } catch (InvalidKeyException ex) {
1405      throw new CMSException("Key error: "+ex.getMessage());
1406    } catch (NoSuchAlgorithmException ex) {
1407      throw new CMSException(ex.toString());
1408    }
1409  }
1410
1411
1412
1413
1414  /**
1415   * Tests the CMS content type implementations <code>Data</code>, <code>EnvelopedData</code>,
1416   * <code>SignedData</code>, <code>DigestedData</code>, <code>EncryptedData</code>.
1417   * An additional <i>SignedAndEncryptedData</i> test sequentially combines
1418   * signed and enveloped data.
1419   */
1420  public void start() {
1421     // the test message
1422    String m = "This is the test message.";
1423    System.out.println("Test message: \""+m+"\"");
1424    System.out.println();
1425    byte[] message = m.getBytes();
1426
1427    try {
1428      byte[] encoding;
1429      byte[] received_message = null;
1430      System.out.println("Stream implementation demos");
1431      System.out.println("===========================");
1432
1433      // the stream implementation
1434      //
1435      // test CMS DataStream
1436      //
1437      System.out.println("\nDataStream demo [create]:\n");
1438      encoding = createDataStream(message);
1439      // transmit data
1440      System.out.println("\nDataStream demo [parse]:\n");
1441      
1442      received_message = getDataStream(encoding);
1443      System.out.print("\nContent: ");
1444      System.out.println(new String(received_message));
1445
1446
1447      // the stream implementation
1448      //
1449      // test CMS EnvelopedDataStream
1450      //
1451      System.out.println("\nEnvelopedDataStream demo [create]:\n");
1452      encoding = createEnvelopedDataStream(message);
1453      // transmit data
1454      System.out.println("\nEnvelopedDataStream demo [parse]:\n");
1455      // user1 means index 0 (hardcoded for this demo)
1456      received_message = getEnvelopedDataStream(encoding, user1_crypt_pk, 0);
1457      System.out.print("\nDecrypted content: ");
1458      System.out.println(new String(received_message));
1459
1460      //
1461      // test CMS Implicit SignedDataStream
1462      //
1463      System.out.println("\nImplicit SignedDataStream demo [create]:\n");
1464      encoding = createSignedDataStream(message, SignedDataStream.IMPLICIT);
1465      // transmit data
1466      System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
1467      received_message = getSignedDataStream(encoding, null);
1468      System.out.print("\nSigned content: ");
1469      System.out.println(new String(received_message));
1470
1471      //
1472      // test CMS Explicit SignedDataStream
1473      //
1474      System.out.println("\nExplicit SignedDataStream demo [create]:\n");
1475      encoding = createSignedDataStream(message, SignedDataStream.EXPLICIT);
1476      // transmit data
1477      System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
1478      received_message = getSignedDataStream(encoding, message);
1479      System.out.print("\nSigned content: ");
1480      System.out.println(new String(received_message));
1481
1482      // test CMS SignedAndEncryptedDataStream
1483      //
1484      System.out.println("\nSignedAndEncryptedDataStream demo [create]:\n");
1485      encoding = createSignedAndEncryptedDataStream(message);
1486      // transmit data
1487      System.out.println("\nSignedAndEncryptedDataStream demo [parse]:\n");
1488      received_message = getSignedAndEncryptedDataStream(encoding);
1489      System.out.print("\nSignedAndEncrypted content: ");
1490      System.out.println(new String(received_message));
1491
1492
1493      //
1494      // test CMS Implicit DigestedDataStream
1495      //
1496      System.out.println("\nImplicit DigestedDataStream demo [create]:\n");
1497      encoding = createDigestedDataStream(message, DigestedDataStream.IMPLICIT);
1498      // transmit data
1499      System.out.println("\nImplicit DigestedDataStream demo [parse]:\n");
1500      received_message = getDigestedDataStream(encoding, null);
1501      System.out.print("\nContent: ");
1502      System.out.println(new String(received_message));
1503
1504      //
1505      // test CMS Explicit DigestedDataStream
1506      //
1507      System.out.println("\nExplicit DigestedDataStream demo [create]:\n");
1508      encoding = createDigestedDataStream(message, DigestedDataStream.EXPLICIT);
1509      // transmit data
1510      System.out.println("\nExplicit DigestedDataStream demo [parse]:\n");
1511      received_message = getDigestedDataStream(encoding, message);
1512      System.out.print("\nContent: ");
1513      System.out.println(new String(received_message));
1514
1515      //
1516      // test CMS EncryptedDataStream
1517      //
1518      System.out.println("\nEncryptedDataStream demo [create]:\n");
1519      encoding = createEncryptedDataStream(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
1520      // transmit data
1521      System.out.println("\nEncryptedDataStream demo [parse]:\n");
1522      received_message = getEncryptedDataStream(encoding, "password".toCharArray());
1523      System.out.print("\nContent: ");
1524      System.out.println(new String(received_message));
1525      
1526            
1527      //
1528      // test CMS Implicit AuthenticatedDataStream with auth attributes
1529      //
1530      System.out.println("\nImplicit AuthenticatedDataStream demo with auth attributes [create]:\n");
1531      encoding = createAuthenticatedDataStream(message, true, AuthenticatedDataStream.IMPLICIT);
1532      // transmit data
1533      System.out.println("\nImplicit AuthenticatedDataStream demo with auth attributes [parse]:\n");
1534      received_message = getAuthenticatedDataStream(encoding, null, user1_crypt_pk, 0);
1535      System.out.print("\nVerified content: ");
1536      System.out.println(new String(received_message));
1537      
1538      System.out.println("\nImplicit AuthenticatedDataStream demo without auth attributes [create]:\n");
1539      encoding = createAuthenticatedDataStream(message, false, AuthenticatedDataStream.IMPLICIT);
1540      // transmit data
1541      System.out.println("\nImplicit AuthenticatedDataStream demo without auth attributes [parse]:\n");
1542      received_message = getAuthenticatedDataStream(encoding, null, user1_crypt_pk, 0);
1543      System.out.print("\nVerified content: ");
1544      System.out.println(new String(received_message));
1545
1546      //
1547      // test CMS Explicit AuthenticatedDataStream
1548      //
1549      System.out.println("\nExplicit AuthenticatedDataStream demo with auth attributes [create]:\n");
1550      encoding = createAuthenticatedDataStream(message, true, AuthenticatedDataStream.EXPLICIT);
1551      // transmit data
1552      System.out.println("\nExplicit AuthenticatedDataStream demo with auth attributes [parse]:\n");
1553      received_message = getAuthenticatedDataStream(encoding, message, user1_crypt_pk, 0);
1554      System.out.print("\nVerified content: ");
1555      System.out.println(new String(received_message));
1556      
1557      System.out.println("\nExplicit AuthenticatedDataStream demo without auth attributes [create]:\n");
1558      encoding = createAuthenticatedDataStream(message, false, AuthenticatedDataStream.EXPLICIT);
1559      // transmit data
1560      System.out.println("\nExplicit AuthenticatedDataStream demo with auth attributes [parse]:\n");
1561      received_message = getAuthenticatedDataStream(encoding, message, user1_crypt_pk, 0);
1562      System.out.print("\nVerified content: ");
1563      System.out.println(new String(received_message));
1564
1565
1566      // the non-stream implementation
1567      System.out.println("\nNon-stream implementation demos");
1568      System.out.println("===============================");
1569      
1570      //
1571      // test CMS Data
1572      //
1573      System.out.println("\nData demo [create]:\n");
1574      encoding = createData(message);
1575      // transmit data
1576      System.out.println("\nData demo [parse]:\n");
1577
1578      received_message = getData(encoding);
1579      System.out.print("\nContent: ");
1580      System.out.println(new String(received_message));
1581
1582      //
1583      // test CMS EnvelopedData
1584      //
1585      System.out.println("\nEnvelopedData demo [create]:\n");
1586      encoding = createEnvelopedData(message);
1587      // transmit data
1588      System.out.println("\nEnvelopedData demo [parse]:\n");
1589      // user1 means index 0 (hardcoded for this demo)
1590      received_message = getEnvelopedData(encoding, user1_crypt_pk, 0);
1591      System.out.print("\nDecrypted content: ");
1592      System.out.println(new String(received_message));
1593
1594      //
1595      // test CMS Implicit SignedData
1596      //
1597      System.out.println("\nImplicit SignedData demo [create]:\n");
1598      encoding = createSignedData(message, SignedDataStream.IMPLICIT);
1599      // transmit data
1600      System.out.println("\nImplicit SignedData demo [parse]:\n");
1601      received_message = getSignedData(encoding, null);
1602      System.out.print("\nSigned content: ");
1603      System.out.println(new String(received_message));
1604
1605      //
1606      // test CMS Explicit SignedData
1607      //
1608      System.out.println("\nExplicit SignedData demo [create]:\n");
1609      encoding = createSignedData(message, SignedData.EXPLICIT);
1610      // transmit data
1611      System.out.println("\nExplicit SignedData demo [parse]:\n");
1612      received_message = getSignedData(encoding, message);
1613      System.out.print("\nSigned content: ");
1614      System.out.println(new String(received_message));
1615
1616      //
1617      // test CMS SignedAndEncryptedData
1618      //
1619      System.out.println("\nSignedAndEncryptedData demo [create]:\n");
1620      encoding = createSignedAndEncryptedData(message);
1621      // transmit data
1622      System.out.println("\nSignedAndEncryptedData demo [parse]:\n");
1623      received_message = getSignedAndEncryptedData(encoding);
1624      System.out.print("\nSignedAndEncrypted content: ");
1625      System.out.println(new String(received_message));
1626
1627
1628      //
1629      // test CMS Implicit DigestedData
1630      //
1631      System.out.println("\nImplicit DigestedData demo [create]:\n");
1632      encoding = createDigestedData(message, DigestedData.IMPLICIT);
1633      // transmit data
1634      System.out.println("\nImplicit DigestedData demo [parse]:\n");
1635      received_message = getDigestedData(encoding, null);
1636      System.out.print("\nContent: ");
1637      System.out.println(new String(received_message));
1638
1639      //
1640      // test CMS Explicit DigestedData
1641      //
1642      System.out.println("\nExplicit DigestedData demo [create]:\n");
1643      encoding = createDigestedData(message, DigestedData.EXPLICIT);
1644      // transmit data
1645      System.out.println("\nExplicit DigestedData demo [parse]:\n");
1646      received_message = getDigestedData(encoding, message);
1647      System.out.print("\nContent: ");
1648      System.out.println(new String(received_message));
1649
1650      //
1651      // test CMS EncryptedData
1652      //
1653      System.out.println("\nEncryptedData demo [create]:\n");
1654      encoding = createEncryptedData(message, (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone(), "password".toCharArray());
1655      // transmit data
1656      System.out.println("\nEncryptedData demo [parse]:\n");
1657      received_message = getEncryptedData(encoding, "password".toCharArray());
1658      System.out.print("\nContent: ");
1659      System.out.println(new String(received_message));
1660      
1661          //
1662      // test CMS Implicit AuthenticatedData 
1663      //
1664      System.out.println("\nImplicit AuthenticatedData demo with auth attributes [create]:\n");
1665      encoding = createAuthenticatedData(message, true, AuthenticatedData.IMPLICIT);
1666      // transmit data
1667      System.out.println("\nImplicit AuthenticatedData demo with auth attributes [parse]:\n");
1668      received_message = getAuthenticatedData(encoding, null, user1_crypt_pk, 0);
1669      System.out.print("\nVerified content: ");
1670      System.out.println(new String(received_message));
1671      
1672      System.out.println("\nImplicit AuthenticatedData demo without auth attributes [create]:\n");
1673      encoding = createAuthenticatedData(message, false, AuthenticatedData.IMPLICIT);
1674      // transmit data
1675      System.out.println("\nImplicit AuthenticatedData demo without auth attributes [parse]:\n");
1676      received_message = getAuthenticatedData(encoding, null, user1_crypt_pk, 0);
1677      System.out.print("\nVerified content: ");
1678      System.out.println(new String(received_message));
1679
1680      //
1681      // test CMS Explicit AuthenticatedData
1682      //
1683      System.out.println("\nExplicit AuthenticatedData demo with auth attributes [create]:\n");
1684      encoding = createAuthenticatedData(message, true, AuthenticatedData.EXPLICIT);
1685      // transmit data
1686      System.out.println("\nExplicit AuthenticatedData demo with auth attributes [parse]:\n");
1687      received_message = getAuthenticatedData(encoding, message, user1_crypt_pk, 0);
1688      System.out.print("\nVerified content: ");
1689      System.out.println(new String(received_message));
1690      
1691      System.out.println("\nExplicit AuthenticatedData demo without auth attributes [create]:\n");
1692      encoding = createAuthenticatedData(message, false, AuthenticatedData.EXPLICIT);
1693      // transmit data
1694      System.out.println("\nExplicit AuthenticatedData demo with auth attributes [parse]:\n");
1695      received_message = getAuthenticatedData(encoding, message, user1_crypt_pk, 0);
1696      System.out.print("\nVerified content: ");
1697      System.out.println(new String(received_message));
1698
1699
1700      System.out.println("Ready!");
1701
1702        } catch (Exception ex) {
1703          ex.printStackTrace();
1704          throw new RuntimeException(ex.toString());
1705        }
1706  }
1707
1708
1709  /**
1710   * Starts the CMS content type implementation tests.
1711   *
1712   * @throws Exception
1713   *            if some error occurs 
1714   */
1715  public static void main(String argv[]) throws Exception {
1716
1717        demo.DemoUtil.initDemos();
1718    (new CMSDemo()).start();
1719
1720    DemoUtil.waitKey();
1721  }
1722}