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/SignedAndEnvelopedDataOutputStreamDemo.java 13    12.02.25 17:58 Dbratko $
059    // $Revision: 13 $
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.CMSException;
068    import iaik.cms.ContentInfoOutputStream;
069    import iaik.cms.EnvelopedDataOutputStream;
070    import iaik.cms.EnvelopedDataStream;
071    import iaik.cms.IssuerAndSerialNumber;
072    import iaik.cms.KeyTransRecipientInfo;
073    import iaik.cms.RecipientInfo;
074    import iaik.cms.SignedDataOutputStream;
075    import iaik.cms.SignedDataStream;
076    import iaik.cms.SignerInfo;
077    import iaik.cms.attributes.CMSContentType;
078    import iaik.cms.attributes.SigningTime;
079    import iaik.security.random.SecRandom;
080    import iaik.utils.Util;
081    import iaik.x509.X509Certificate;
082    
083    import java.io.ByteArrayInputStream;
084    import java.io.ByteArrayOutputStream;
085    import java.io.IOException;
086    import java.io.InputStream;
087    import java.security.NoSuchAlgorithmException;
088    import java.security.PrivateKey;
089    import java.security.SecureRandom;
090    
091    import demo.DemoUtil;
092    import demo.keystore.CMSKeyStore;
093    
094    /**
095     * This class shows the sequential combination of the SignedDataOutputStream
096     * and EnvelopedDataOutputStream implementations.
097     * <p>
098     * All keys and certificates are read from a keystore created by the
099     * SetupCMSKeyStore program.
100     */
101    public class SignedAndEnvelopedDataOutputStreamDemo {
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      // secure random number generator
118      SecureRandom random;
119    
120      /**
121       * Setup the demo certificate chains.
122       *
123       * Keys and certificate are retrieved from the demo KeyStore.
124       *
125       * @throws IOException if an file read error occurs
126       */
127      public SignedAndEnvelopedDataOutputStreamDemo() throws IOException {
128        
129        System.out.println();
130        System.out.println("*********************************************************************************************************");
131        System.out.println("*                                   SignedAndEnvelopedDataOutputStream demo                             *");
132        System.out.println("* (shows the usage of the combined SignedDataOutputStream and EnvelopedDataOutputStream implementation) *");
133        System.out.println("*********************************************************************************************************");
134        System.out.println();
135        
136        // add all certificates to the list
137        X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
138        user1_sign = certs[0];
139        user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
140        user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
141        user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
142        user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
143        user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
144        // user1 also includes her/his encryption certificate
145        certificates = new X509Certificate[certs.length+1];
146        System.arraycopy(certs, 0, certificates, 0, certs.length);
147        certificates[certs.length] = user1_crypt;
148    
149        random = SecRandom.getDefault();
150      }
151    
152      /**
153       * Signs and envelopes the given message, encodes the CMS object, decodes it 
154       * again, removes the envlope and verifies the signature.
155       * 
156       * @param message the message to be signed and enveloped
157       * @throws Exception if an error occurs
158       */
159      public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
160        
161        //  a stream from which to read the data to be signed and encrypted
162        ByteArrayInputStream is = new ByteArrayInputStream(message);
163        
164        // the stream to which to write the Signed and EnvelopedData 
165        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
166    
167        EnvelopedDataOutputStream envelopedData;
168    
169        //  wrap into a ContentInfo 
170        ContentInfoOutputStream envContentInfoStream = 
171          new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
172        // create a new EnvelopedData object encrypted with AES
173        try {
174          envelopedData = new EnvelopedDataOutputStream(envContentInfoStream, 
175                                                        (AlgorithmID)AlgorithmID.aes256_CBC.clone());
176        } catch (NoSuchAlgorithmException ex) {
177          throw new CMSException(ex.toString());
178        }
179    
180        // create the recipient infos
181        RecipientInfo[] recipients = new RecipientInfo[2];
182        // user1 is the first receiver
183        recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
184        // user2 is the second receiver
185        recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
186    
187        // specify the recipients of the encrypted message
188        envelopedData.setRecipientInfos(recipients);
189        
190        //  wrap SignedData into a ContentInfo 
191        ContentInfoOutputStream signedContentInfoStream = 
192          new ContentInfoOutputStream(ObjectID.cms_signedData, envelopedData);
193    
194        SignedDataOutputStream signedData = new SignedDataOutputStream(signedContentInfoStream, 
195                                                                       SignedDataOutputStream.IMPLICIT);
196        // these certificates are sent within the signature
197        signedData.setCertificates(certificates);
198    
199        // add one signer
200        // cert at index 0 is the user certificate
201        IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
202    
203        // create a new SignerInfo
204        SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
205          
206        // create some signed attributes
207        // the message digest attribute is automatically added
208        Attribute[] attributes = new Attribute[2];
209        // content type is data
210        CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
211        attributes[0] = new Attribute(contentType);
212        // signing time is now
213        SigningTime signingTime = new SigningTime();
214        attributes[1] = new Attribute(signingTime);
215      
216        // set the attributes
217        signer_info.setSignedAttributes(attributes);
218        // finish the creation of SignerInfo by calling method addSigner
219        signedData.addSignerInfo(signer_info);
220    
221        int blockSize = 4; // in real world we would use a block size like 2048
222        //  write in the data to be signed
223        byte[] buffer = new byte[blockSize];
224        int bytesRead;
225        while ((bytesRead = is.read(buffer)) != -1) {
226          signedData.write(buffer, 0, bytesRead);
227        }
228        
229        // closing the stream finished the cryptographic processing and closes the underlying stream
230        signedData.close();  
231        
232        // get the signed and encrypted message from the ByteArrayOutputStream
233        byte[]  signed_enveloped_message = resultStream.toByteArray();
234        System.out.println("Message created, now doing the parsing...");
235        // and now decrypt the data and verify the signature
236        is = new ByteArrayInputStream(signed_enveloped_message);
237        EnvelopedDataStream enveloped = new EnvelopedDataStream(is);
238        // use this private key to decrypt the symmetric key of recipient 0
239        enveloped.setupCipher(user1_crypt_pk, 0);
240        // get the InputStream with the decrypted data
241        InputStream data_dec = enveloped.getInputStream();
242        System.out.println("Message decrypted!");
243        // read the signed data from the decrypted InputStream
244        SignedDataStream  signed = new SignedDataStream(data_dec);
245        // get the InputStream with the signed, plain data
246        InputStream data = signed.getInputStream();
247        // reset our output stream
248        ByteArrayOutputStream os = new ByteArrayOutputStream();
249        // copy the data
250        Util.copyStream(data, os, null);
251        os.close();
252        is.close();
253        data_dec.close();
254    
255        // now verify the signature of the one and only signer and print the certificate of the signer
256        X509Certificate cert = signed.verify(0);
257        System.out.println("Signature OK from: "+cert.getSubjectDN());
258    
259        System.out.println("Received message: \"" + new String(os.toByteArray())+"\"");
260      }
261    
262    
263    
264      /**
265       * Starts the test.
266       */
267      public void start() {
268         // the test message
269        String m = "This demo message will be signed and/or encrypted.";
270        System.out.println("Test message: \""+m+"\"");
271        System.out.println();
272        byte[] message = m.getBytes();
273    
274        try {
275          testSignedAndEnvelopedDataStream(message);
276          System.out.println();
277    
278          System.out.println("Ready!");
279            } catch (Exception ex) {
280              ex.printStackTrace();
281    
282              throw new RuntimeException(ex.toString());
283            }
284      }
285    
286      /**
287       * Main method.
288       *
289       * @throws IOException
290       *            if an I/O error occurs when reading required keys
291       *            and certificates from files
292       */
293      public static void main(String argv[]) throws Exception {
294    
295            demo.DemoUtil.initDemos();
296    
297        (new SignedAndEnvelopedDataOutputStreamDemo()).start();
298        DemoUtil.waitKey();
299      }
300    
301    }