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/signedAndEnvelopedData/SignedAndEnvelopedDataDemo.java 26    12.02.25 17:58 Dbratko $
029// $Revision: 26 $
030//
031
032package demo.cms.signedAndEnvelopedData;
033
034import iaik.asn1.ObjectID;
035import iaik.asn1.structures.AlgorithmID;
036import iaik.asn1.structures.Attribute;
037import iaik.cms.EnvelopedData;
038import iaik.cms.EnvelopedDataStream;
039import iaik.cms.IssuerAndSerialNumber;
040import iaik.cms.KeyTransRecipientInfo;
041import iaik.cms.RecipientInfo;
042import iaik.cms.SignedData;
043import iaik.cms.SignedDataStream;
044import iaik.cms.SignerInfo;
045import iaik.cms.attributes.CMSContentType;
046import iaik.cms.attributes.SigningTime;
047import iaik.utils.Util;
048import iaik.x509.X509Certificate;
049
050import java.io.BufferedInputStream;
051import java.io.BufferedOutputStream;
052import java.io.ByteArrayInputStream;
053import java.io.ByteArrayOutputStream;
054import java.io.IOException;
055import java.io.InputStream;
056import java.io.OutputStream;
057import java.io.PipedInputStream;
058import java.io.PipedOutputStream;
059import java.security.PrivateKey;
060
061import demo.DemoUtil;
062import demo.keystore.CMSKeyStore;
063
064/**
065 * This class shows the sequential combination of the SignedData and EnvelopedData
066 * implementations.
067 * <p>
068 * All keys and certificates are read from a keystore created by the
069 * SetupCMSKeyStore program.
070 */
071public class SignedAndEnvelopedDataDemo {
072
073  // signing certificate of user 1
074  X509Certificate user1_sign;
075  // signing private key of user 1
076  PrivateKey user1_sign_pk;
077  // encryption certificate of user 1
078  X509Certificate user1_crypt;
079  // encryption private key of user 1
080  PrivateKey user1_crypt_pk;
081  // encryption certificate of user 2
082  X509Certificate user2_crypt;
083  // encryption private key of user 2
084  PrivateKey user2_crypt_pk;
085  // a certificate chain containing the user certs + CA
086  X509Certificate[] certificates;
087
088  /**
089   * Setup the demo certificate chains.
090   *
091   * Keys and certificate are retrieved from the demo KeyStore.
092   *
093   * @throws IOException if an file read error occurs
094   */
095  public SignedAndEnvelopedDataDemo() throws IOException {
096    
097    System.out.println();
098    System.out.println("*************************************************************************************************");
099    System.out.println("*                               SignedAndEnvelopedDataDemo                                      *");
100    System.out.println("* (shows the usage of the combined SignedData(Stream) and EnvelopedData(Stream) implementation) *");
101    System.out.println("*************************************************************************************************");
102    System.out.println();
103    
104    // add all certificates to the list
105    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
106    user1_sign = certs[0];
107    user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
108    user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
109    user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
110    user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
111    user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
112    // user1 also includes her/his encryption certificate
113    certificates = new X509Certificate[certs.length+1];
114    System.arraycopy(certs, 0, certificates, 0, certs.length);
115    certificates[certs.length] = user1_crypt;
116
117  }
118
119  /**
120   * Uses the stream based SignedData and EnvelopedData implementations
121   * (<code>SignedDataStream</code>, <code>EnvelopedDataStream</code> to 
122   * sign and envelope the given message, encode the CMS object, decodes it 
123   * again, removes the envlope and verifies the signature.
124   * 
125   * @param message the message to be signed and enveloped
126   * @throws Exception if an error occurs
127   */
128  public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
129    // repository for the signed and enveloped message
130    byte[] signed_enveloped_message;
131    // the InputStream containing the data to sign and encrypt
132    InputStream is = new BufferedInputStream(new ByteArrayInputStream(message));
133    // the OutputStream where the data shall be written to
134    ByteArrayOutputStream out = new ByteArrayOutputStream();
135    OutputStream os = new BufferedOutputStream(out);
136
137
138    // create an implicit signed message (signature contains message)
139    SignedDataStream signed = new SignedDataStream(is, SignedDataStream.IMPLICIT);
140
141    // these certificates are sent within the signature
142    signed.setCertificates(certificates);
143
144    // add one signer
145    // cert at index 0 is the user certificate
146    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
147
148    // create a new SignerInfo
149    SignerInfo signer_info = new SignerInfo(issuer, AlgorithmID.sha256, user1_sign_pk);
150    
151    // create some signed attributes
152    // the message digest attribute is automatically added
153    Attribute[] attributes = new Attribute[2];
154    // content type is data
155    CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
156    attributes[0] = new Attribute(contentType);
157    // signing time is now
158    SigningTime signingTime = new SigningTime();
159    attributes[1] = new Attribute(signingTime);
160 
161    // set the attributes
162    signer_info.setSignedAttributes(attributes);
163    // finish the creation of SignerInfo by calling method addSigner
164    signed.addSignerInfo(signer_info);
165
166     // we have to sign and encrypt => connect 2 streams
167    PipedOutputStream piped_out = new PipedOutputStream();
168    PipedInputStream piped_in = new PipedInputStream(piped_out);
169    // a new Thread between the 2 streams
170    Writer writer = new Writer(signed, piped_out);
171    writer.start();
172
173    // encrypt with AES
174    EnvelopedDataStream enveloped = new EnvelopedDataStream(piped_in, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
175    // add recipients where the symmetric key is encrypted with RSA
176        // create the recipient infos
177    RecipientInfo[] recipients = new RecipientInfo[2];
178    // user1 is the first receiver
179    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
180    // user2 is the second receiver
181    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
182
183    // specify the recipients of the encrypted message
184    enveloped.setRecipientInfos(recipients);
185
186    // encrypt and write the data to the output stream
187    enveloped.writeTo(os,2048);
188    // finished
189    os.close();
190    is.close();
191    // get the signed and encrypted message from the ByteArrayOutputStream
192    signed_enveloped_message = out.toByteArray();
193    System.out.println("Message created, now doing the parsing...");
194    // and now decrypt the data and verify the signature
195    is = new BufferedInputStream(new ByteArrayInputStream( signed_enveloped_message));
196    enveloped = new EnvelopedDataStream(is);
197    // use this private key to decrypt the symmetric key of recipient 0
198    enveloped.setupCipher(user1_crypt_pk, 0);
199    // get the InputStream with the decrypted data
200    InputStream data_dec = enveloped.getInputStream();
201    System.out.println("Message decrypted!");
202    // read the signed data from the decrypted InputStream
203    signed = new SignedDataStream(data_dec);
204    // get the InputStream with the signed, plain data
205    InputStream data = signed.getInputStream();
206
207    // reset our output stream
208    out.reset();
209    // write the decrypted and verified data to the output stream
210    os = new BufferedOutputStream(out);
211    // copy the data
212    Util.copyStream(data, os, null);
213    os.close();
214    out.close();
215    is.close();
216    data_dec.close();
217
218    // now verify the signature of the one and only signer and print the certificate of the signer
219    X509Certificate cert = signed.verify(0);
220    System.out.println("Signature OK from: "+cert.getSubjectDN());
221
222
223    System.out.println("Received message: \"" + new String(out.toByteArray())+"\"");
224  }
225  
226  /**
227   * Uses the non-stream based SignedData and EnvelopedData implementations
228   * (<code>SignedData</code>, <code>EnvelopedData</code> to 
229   * sign and envelope the given message, encode the CMS object, decodes it 
230   * again, removes the envlope and verifies the signature.
231   * 
232   * @param message the message to be signed and enveloped
233   * @throws Exception if an error occurs
234   */
235  public void testSignedAndEnvelopedData(byte[] message) throws Exception {
236        
237    // create an implicit signed message (signature contains message)
238    SignedData signed = new SignedData(message, SignedData.IMPLICIT);
239
240    // these certificates are sent within the signature
241    signed.setCertificates(certificates);
242
243    // add one signer
244    // cert at index 0 is the user certificate
245    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
246
247    // create a new SignerInfo
248    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
249    
250    // create some signed attributes
251    // the message digest attribute is automatically added
252    Attribute[] attributes = new Attribute[2];
253    // content type is data
254    CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
255    attributes[0] = new Attribute(contentType);
256    // signing time is now
257    SigningTime signingTime = new SigningTime();
258    attributes[1] = new Attribute(signingTime);
259 
260    // set the attributes
261    signer_info.setSignedAttributes(attributes);
262    // finish the creation of SignerInfo by calling method addSigner
263    signed.addSignerInfo(signer_info);
264
265    // encode SignedData to a byte array
266    byte[] signed_message = signed.getEncoded();
267
268    // encrypt with AES
269    EnvelopedData enveloped = new EnvelopedData(signed_message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
270    // add recipients where the symmetric key is encrypted with RSA
271        // create the recipient infos
272    RecipientInfo[] recipients = new RecipientInfo[2];
273    // user1 is the first receiver
274    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
275    // user2 is the second receiver
276    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
277
278    // specify the recipients of the encrypted message
279    enveloped.setRecipientInfos(recipients);
280
281    // encrypt and write the data to a byte array
282    byte[] signed_enveloped_message = enveloped.getEncoded();
283
284    System.out.println("Message created, now doing the parsing...");
285    // and now decrypt the data and verify the signature
286    InputStream is = new BufferedInputStream(new ByteArrayInputStream(signed_enveloped_message));
287    enveloped = new EnvelopedData(is);
288    // use this private key to decrypt the symmetric key of recipient 0
289    enveloped.setupCipher(user1_crypt_pk, 0);
290    // get the InputStream with the decrypted data
291    InputStream data_dec = enveloped.getInputStream();
292    System.out.println("Message decrypted!");
293    // read the signed data from the decrypted InputStream
294    signed = new SignedData(data_dec);
295    // get the content with the signed, plain data
296    byte[] data = signed.getContent();
297
298    // now verify the signature of the one and only signer and print the certificate of the signer
299    X509Certificate cert = signed.verify(0);
300    System.out.println("Signature OK from: "+cert.getSubjectDN());
301
302
303    System.out.println("Received message: \"" + new String(data)+"\"");
304  }
305
306
307
308
309  /**
310   * Starts the test.
311   */
312  public void start() {
313     // the test message
314    String m = "This demo message will be signed and/or encrypted.";
315    System.out.println("Test message: \""+m+"\"");
316    System.out.println();
317    byte[] message = m.getBytes();
318
319    try {
320      System.out.println("Signed and enveloped data - stream based demo.");
321      testSignedAndEnvelopedDataStream(message);
322      System.out.println();
323      
324      System.out.println("Signed and enveloped data - non stream based demo.");
325      testSignedAndEnvelopedData(message);
326      System.out.println();
327
328      System.out.println("Ready!");
329        } catch (Exception ex) {
330          ex.printStackTrace();
331
332          throw new RuntimeException(ex.toString());
333        }
334  }
335
336  /**
337   * Main method.
338   *
339   * @throws IOException
340   *            if an I/O error occurs when reading required keys
341   *            and certificates from files
342   */
343  public static void main(String argv[]) throws Exception {
344
345        demo.DemoUtil.initDemos();
346
347    (new SignedAndEnvelopedDataDemo()).start();
348    DemoUtil.waitKey();
349  }
350
351  /**
352   * Inner class for copying data between the 2 streams.
353   */
354  static class Writer extends Thread {
355
356    SignedDataStream signed;
357    OutputStream os;
358    Exception exception;
359
360    public Writer(SignedDataStream signed, OutputStream os) {
361      super("Writer");
362      this.signed = signed;
363      this.os = os;
364    }
365
366    /**
367     * Writes the SMimeSinged to the OutputStream.
368     */
369    public void run() {
370      try {
371        signed.writeTo(os,2048);
372        os.close();
373      } catch (Exception ex) {
374        exception = ex;
375        System.out.println("Writer exception: "+exception);
376      }
377    }
378
379    /**
380     * Returns a possible exception.
381     */
382    public Exception getException() {
383      return exception;
384    }
385  }
386
387}