001    // Copyright (C) 2002 IAIK
002    // https://jce.iaik.tugraz.at
003    //
004    // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005    //                           Communication Technologies SIC
006    // https://sic.tech
007    //
008    // All rights reserved.
009    //
010    // Redistribution and use in source and binary forms, with or without
011    // modification, are permitted provided that the following conditions
012    // are met:
013    // 1. Redistributions of source code must retain the above copyright
014    //    notice, this list of conditions and the following disclaimer.
015    // 2. Redistributions in binary form must reproduce the above copyright
016    //    notice, this list of conditions and the following disclaimer in the
017    //    documentation and/or other materials provided with the distribution.
018    //
019    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029    // SUCH DAMAGE.
030    
031    // Copyright (C) 2002 IAIK
032    // https://sic.tech/
033    //
034    // Copyright (C) 2003 - 2025 Stiftung Secure Information and 
035    //                           Communication Technologies SIC
036    // https://sic.tech/
037    //
038    // All rights reserved.
039    //
040    // This source is provided for inspection purposes and recompilation only,
041    // unless specified differently in a contract with IAIK. This source has to
042    // be kept in strict confidence and must not be disclosed to any third party
043    // under any circumstances. Redistribution in source and binary forms, with
044    // or without modification, are <not> permitted in any case!
045    //
046    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056    // SUCH DAMAGE.
057    //
058    // $Header: /IAIK-CMS/current/src/demo/cms/signedAndEnvelopedData/SignedAndEnvelopedDataDemo.java 26    12.02.25 17:58 Dbratko $
059    // $Revision: 26 $
060    //
061    
062    package demo.cms.signedAndEnvelopedData;
063    
064    import iaik.asn1.ObjectID;
065    import iaik.asn1.structures.AlgorithmID;
066    import iaik.asn1.structures.Attribute;
067    import iaik.cms.EnvelopedData;
068    import iaik.cms.EnvelopedDataStream;
069    import iaik.cms.IssuerAndSerialNumber;
070    import iaik.cms.KeyTransRecipientInfo;
071    import iaik.cms.RecipientInfo;
072    import iaik.cms.SignedData;
073    import iaik.cms.SignedDataStream;
074    import iaik.cms.SignerInfo;
075    import iaik.cms.attributes.CMSContentType;
076    import iaik.cms.attributes.SigningTime;
077    import iaik.utils.Util;
078    import iaik.x509.X509Certificate;
079    
080    import java.io.BufferedInputStream;
081    import java.io.BufferedOutputStream;
082    import java.io.ByteArrayInputStream;
083    import java.io.ByteArrayOutputStream;
084    import java.io.IOException;
085    import java.io.InputStream;
086    import java.io.OutputStream;
087    import java.io.PipedInputStream;
088    import java.io.PipedOutputStream;
089    import java.security.PrivateKey;
090    
091    import demo.DemoUtil;
092    import demo.keystore.CMSKeyStore;
093    
094    /**
095     * This class shows the sequential combination of the SignedData and EnvelopedData
096     * implementations.
097     * <p>
098     * All keys and certificates are read from a keystore created by the
099     * SetupCMSKeyStore program.
100     */
101    public class SignedAndEnvelopedDataDemo {
102    
103      // signing certificate of user 1
104      X509Certificate user1_sign;
105      // signing private key of user 1
106      PrivateKey user1_sign_pk;
107      // encryption certificate of user 1
108      X509Certificate user1_crypt;
109      // encryption private key of user 1
110      PrivateKey user1_crypt_pk;
111      // encryption certificate of user 2
112      X509Certificate user2_crypt;
113      // encryption private key of user 2
114      PrivateKey user2_crypt_pk;
115      // a certificate chain containing the user certs + CA
116      X509Certificate[] certificates;
117    
118      /**
119       * Setup the demo certificate chains.
120       *
121       * Keys and certificate are retrieved from the demo KeyStore.
122       *
123       * @throws IOException if an file read error occurs
124       */
125      public SignedAndEnvelopedDataDemo() throws IOException {
126        
127        System.out.println();
128        System.out.println("*************************************************************************************************");
129        System.out.println("*                               SignedAndEnvelopedDataDemo                                      *");
130        System.out.println("* (shows the usage of the combined SignedData(Stream) and EnvelopedData(Stream) implementation) *");
131        System.out.println("*************************************************************************************************");
132        System.out.println();
133        
134        // add all certificates to the list
135        X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
136        user1_sign = certs[0];
137        user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
138        user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
139        user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
140        user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
141        user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
142        // user1 also includes her/his encryption certificate
143        certificates = new X509Certificate[certs.length+1];
144        System.arraycopy(certs, 0, certificates, 0, certs.length);
145        certificates[certs.length] = user1_crypt;
146    
147      }
148    
149      /**
150       * Uses the stream based SignedData and EnvelopedData implementations
151       * (<code>SignedDataStream</code>, <code>EnvelopedDataStream</code> to 
152       * sign and envelope the given message, encode the CMS object, decodes it 
153       * again, removes the envlope and verifies the signature.
154       * 
155       * @param message the message to be signed and enveloped
156       * @throws Exception if an error occurs
157       */
158      public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
159        // repository for the signed and enveloped message
160        byte[] signed_enveloped_message;
161        // the InputStream containing the data to sign and encrypt
162        InputStream is = new BufferedInputStream(new ByteArrayInputStream(message));
163        // the OutputStream where the data shall be written to
164        ByteArrayOutputStream out = new ByteArrayOutputStream();
165        OutputStream os = new BufferedOutputStream(out);
166    
167    
168        // create an implicit signed message (signature contains message)
169        SignedDataStream signed = new SignedDataStream(is, SignedDataStream.IMPLICIT);
170    
171        // these certificates are sent within the signature
172        signed.setCertificates(certificates);
173    
174        // add one signer
175        // cert at index 0 is the user certificate
176        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
177    
178        // create a new SignerInfo
179        SignerInfo signer_info = new SignerInfo(issuer, AlgorithmID.sha256, user1_sign_pk);
180        
181        // create some signed attributes
182        // the message digest attribute is automatically added
183        Attribute[] attributes = new Attribute[2];
184        // content type is data
185        CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
186        attributes[0] = new Attribute(contentType);
187        // signing time is now
188        SigningTime signingTime = new SigningTime();
189        attributes[1] = new Attribute(signingTime);
190     
191        // set the attributes
192        signer_info.setSignedAttributes(attributes);
193        // finish the creation of SignerInfo by calling method addSigner
194        signed.addSignerInfo(signer_info);
195    
196         // we have to sign and encrypt => connect 2 streams
197        PipedOutputStream piped_out = new PipedOutputStream();
198        PipedInputStream piped_in = new PipedInputStream(piped_out);
199        // a new Thread between the 2 streams
200        Writer writer = new Writer(signed, piped_out);
201        writer.start();
202    
203        // encrypt with AES
204        EnvelopedDataStream enveloped = new EnvelopedDataStream(piped_in, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
205        // add recipients where the symmetric key is encrypted with RSA
206            // create the recipient infos
207        RecipientInfo[] recipients = new RecipientInfo[2];
208        // user1 is the first receiver
209        recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
210        // user2 is the second receiver
211        recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
212    
213        // specify the recipients of the encrypted message
214        enveloped.setRecipientInfos(recipients);
215    
216        // encrypt and write the data to the output stream
217        enveloped.writeTo(os,2048);
218        // finished
219        os.close();
220        is.close();
221        // get the signed and encrypted message from the ByteArrayOutputStream
222        signed_enveloped_message = out.toByteArray();
223        System.out.println("Message created, now doing the parsing...");
224        // and now decrypt the data and verify the signature
225        is = new BufferedInputStream(new ByteArrayInputStream( signed_enveloped_message));
226        enveloped = new EnvelopedDataStream(is);
227        // use this private key to decrypt the symmetric key of recipient 0
228        enveloped.setupCipher(user1_crypt_pk, 0);
229        // get the InputStream with the decrypted data
230        InputStream data_dec = enveloped.getInputStream();
231        System.out.println("Message decrypted!");
232        // read the signed data from the decrypted InputStream
233        signed = new SignedDataStream(data_dec);
234        // get the InputStream with the signed, plain data
235        InputStream data = signed.getInputStream();
236    
237        // reset our output stream
238        out.reset();
239        // write the decrypted and verified data to the output stream
240        os = new BufferedOutputStream(out);
241        // copy the data
242        Util.copyStream(data, os, null);
243        os.close();
244        out.close();
245        is.close();
246        data_dec.close();
247    
248        // now verify the signature of the one and only signer and print the certificate of the signer
249        X509Certificate cert = signed.verify(0);
250        System.out.println("Signature OK from: "+cert.getSubjectDN());
251    
252    
253        System.out.println("Received message: \"" + new String(out.toByteArray())+"\"");
254      }
255      
256      /**
257       * Uses the non-stream based SignedData and EnvelopedData implementations
258       * (<code>SignedData</code>, <code>EnvelopedData</code> to 
259       * sign and envelope the given message, encode the CMS object, decodes it 
260       * again, removes the envlope and verifies the signature.
261       * 
262       * @param message the message to be signed and enveloped
263       * @throws Exception if an error occurs
264       */
265      public void testSignedAndEnvelopedData(byte[] message) throws Exception {
266            
267        // create an implicit signed message (signature contains message)
268        SignedData signed = new SignedData(message, SignedData.IMPLICIT);
269    
270        // these certificates are sent within the signature
271        signed.setCertificates(certificates);
272    
273        // add one signer
274        // cert at index 0 is the user certificate
275        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
276    
277        // create a new SignerInfo
278        SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
279        
280        // create some signed attributes
281        // the message digest attribute is automatically added
282        Attribute[] attributes = new Attribute[2];
283        // content type is data
284        CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
285        attributes[0] = new Attribute(contentType);
286        // signing time is now
287        SigningTime signingTime = new SigningTime();
288        attributes[1] = new Attribute(signingTime);
289     
290        // set the attributes
291        signer_info.setSignedAttributes(attributes);
292        // finish the creation of SignerInfo by calling method addSigner
293        signed.addSignerInfo(signer_info);
294    
295        // encode SignedData to a byte array
296        byte[] signed_message = signed.getEncoded();
297    
298        // encrypt with AES
299        EnvelopedData enveloped = new EnvelopedData(signed_message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
300        // add recipients where the symmetric key is encrypted with RSA
301            // create the recipient infos
302        RecipientInfo[] recipients = new RecipientInfo[2];
303        // user1 is the first receiver
304        recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
305        // user2 is the second receiver
306        recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
307    
308        // specify the recipients of the encrypted message
309        enveloped.setRecipientInfos(recipients);
310    
311        // encrypt and write the data to a byte array
312        byte[] signed_enveloped_message = enveloped.getEncoded();
313    
314        System.out.println("Message created, now doing the parsing...");
315        // and now decrypt the data and verify the signature
316        InputStream is = new BufferedInputStream(new ByteArrayInputStream(signed_enveloped_message));
317        enveloped = new EnvelopedData(is);
318        // use this private key to decrypt the symmetric key of recipient 0
319        enveloped.setupCipher(user1_crypt_pk, 0);
320        // get the InputStream with the decrypted data
321        InputStream data_dec = enveloped.getInputStream();
322        System.out.println("Message decrypted!");
323        // read the signed data from the decrypted InputStream
324        signed = new SignedData(data_dec);
325        // get the content with the signed, plain data
326        byte[] data = signed.getContent();
327    
328        // now verify the signature of the one and only signer and print the certificate of the signer
329        X509Certificate cert = signed.verify(0);
330        System.out.println("Signature OK from: "+cert.getSubjectDN());
331    
332    
333        System.out.println("Received message: \"" + new String(data)+"\"");
334      }
335    
336    
337    
338    
339      /**
340       * Starts the test.
341       */
342      public void start() {
343         // the test message
344        String m = "This demo message will be signed and/or encrypted.";
345        System.out.println("Test message: \""+m+"\"");
346        System.out.println();
347        byte[] message = m.getBytes();
348    
349        try {
350          System.out.println("Signed and enveloped data - stream based demo.");
351          testSignedAndEnvelopedDataStream(message);
352          System.out.println();
353          
354          System.out.println("Signed and enveloped data - non stream based demo.");
355          testSignedAndEnvelopedData(message);
356          System.out.println();
357    
358          System.out.println("Ready!");
359            } catch (Exception ex) {
360              ex.printStackTrace();
361    
362              throw new RuntimeException(ex.toString());
363            }
364      }
365    
366      /**
367       * Main method.
368       *
369       * @throws IOException
370       *            if an I/O error occurs when reading required keys
371       *            and certificates from files
372       */
373      public static void main(String argv[]) throws Exception {
374    
375            demo.DemoUtil.initDemos();
376    
377        (new SignedAndEnvelopedDataDemo()).start();
378        DemoUtil.waitKey();
379      }
380    
381      /**
382       * Inner class for copying data between the 2 streams.
383       */
384      static class Writer extends Thread {
385    
386        SignedDataStream signed;
387        OutputStream os;
388        Exception exception;
389    
390        public Writer(SignedDataStream signed, OutputStream os) {
391          super("Writer");
392          this.signed = signed;
393          this.os = os;
394        }
395    
396        /**
397         * Writes the SMimeSinged to the OutputStream.
398         */
399        public void run() {
400          try {
401            signed.writeTo(os,2048);
402            os.close();
403          } catch (Exception ex) {
404            exception = ex;
405            System.out.println("Writer exception: "+exception);
406          }
407        }
408    
409        /**
410         * Returns a possible exception.
411         */
412        public Exception getException() {
413          return exception;
414        }
415      }
416    
417    }