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/encryptedData/EncryptedDataOutputStreamDemo.java 8     12.02.25 17:58 Dbratko $
059    // $Revision: 8 $
060    //
061    
062    
063    package demo.cms.encryptedData;
064    
065    import iaik.asn1.CodingException;
066    import iaik.asn1.ObjectID;
067    import iaik.asn1.structures.AlgorithmID;
068    import iaik.asn1.structures.Attribute;
069    import iaik.cms.CMSException;
070    import iaik.cms.ContentInfoOutputStream;
071    import iaik.cms.EncryptedContentInfoStream;
072    import iaik.cms.EncryptedDataOutputStream;
073    import iaik.cms.EncryptedDataStream;
074    import iaik.cms.attributes.CMSContentType;
075    import iaik.security.random.SecRandom;
076    import iaik.utils.CryptoUtils;
077    import iaik.utils.Util;
078    
079    import java.io.ByteArrayInputStream;
080    import java.io.ByteArrayOutputStream;
081    import java.io.IOException;
082    import java.io.InputStream;
083    import java.security.InvalidAlgorithmParameterException;
084    import java.security.InvalidKeyException;
085    import java.security.NoSuchAlgorithmException;
086    import java.security.SecureRandom;
087    import java.security.spec.InvalidParameterSpecException;
088    
089    import demo.DemoUtil;
090    
091    
092    /**
093     * Demonstrates the usage of class {@link iaik.cms.EncryptedDataOutputStream} for
094     * PBE encrypting data using the CMS type EnryptedData.
095     */
096    public class EncryptedDataOutputStreamDemo {
097    
098      // secure random number generator
099      SecureRandom random;
100    
101      /**
102       * Default constructor.
103       */
104      public EncryptedDataOutputStreamDemo() {
105        
106        System.out.println();
107        System.out.println("**********************************************************************************");
108        System.out.println("*                    EncryptedDataOutputStream demo                              *");
109        System.out.println("*    (shows the usage of the CMS EncryptedDataOutputStream implementation)       *");
110        System.out.println("**********************************************************************************");
111        System.out.println();
112        
113        random = SecRandom.getDefault();
114    
115      }
116    
117    
118      /**
119       * Creates a CMS <code>EncryptedData</code> and wraps it into a ContentInfo.
120       *
121       * @param message the message to be encrypted, as byte representation
122       * @return the encoded EncryptedData object just created, wrapped into a ContentInfo
123       *
124       * @throws CMSException if the <code>EncryptedData</code> object cannot
125       *                          be created
126       * @throws IOException if an I/O error occurs
127       */
128      public byte[] createEncryptedData(byte[] message, char[] password) throws CMSException, IOException {
129        
130        
131        //  a stream from which to read the data to be encrypted
132        ByteArrayInputStream is = new ByteArrayInputStream(message);
133        
134        // the stream to which to write the EncryptedData
135        ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
136        
137        //  wrap EncryptedData into a ContentInfo 
138        ContentInfoOutputStream contentInfoStream = 
139          new ContentInfoOutputStream(ObjectID.cms_encryptedData, resultStream);
140        
141        // create a new EncryptedData object 
142        EncryptedDataOutputStream  encryptedData = new EncryptedDataOutputStream(contentInfoStream);
143        // setup cipher for encryption
144        AlgorithmID contentEncAlg = (AlgorithmID)AlgorithmID.pbeWithSHAAnd3_KeyTripleDES_CBC.clone();
145        try {
146          encryptedData.setupCipher(contentEncAlg, password, 2000);
147        } catch (InvalidKeyException ex) {
148          throw new CMSException("Cannot setup cipher for encryption: " + ex.toString());
149        } catch (NoSuchAlgorithmException ex) {
150          throw new CMSException("Cannot setup cipher for encryption: " + ex.toString());  
151        }
152        Attribute[] attributes = new Attribute[1];
153        try {
154          // just for demonstration: set some unprotected attribute
155          // content type is data
156          CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
157          attributes[0] = new Attribute(contentType);
158          encryptedData.setUnprotectedAttributes(attributes);
159        } catch (Exception ex) {
160          throw new CMSException("Error creating attribute: " + ex.toString());   
161        }  
162    
163        int blockSize = 8; // in real world we would use a block size like 2048
164        //  write in the data to be encrypted
165        byte[] buffer = new byte[blockSize];
166        int bytesRead;
167        while ((bytesRead = is.read(buffer)) != -1) {
168          encryptedData.write(buffer, 0, bytesRead);
169        }
170        
171        // closing the stream finishes encryption and closes the underlying stream
172        encryptedData.close();
173        return resultStream.toByteArray();
174      }
175    
176      /**
177       * Decrypts the encrypted content of the given EncryptedData object.
178       *
179       * @param encoding the encoded EncryptedData object, wrapped in a ContentInfo
180       * @param password the password to decrypt the message
181       *
182       * @return the recovered message, as byte array
183       * 
184       * @throws CMSException if the message cannot be recovered
185       * @throws IOException if an I/O error occurs
186       */
187      public byte[] getEncryptedData(byte[] encoding, char[] password) throws CMSException, IOException {
188    
189        //  create the EncryptpedData object from a BER encoded byte array
190        // we are testing the stream interface
191        ByteArrayInputStream is = new ByteArrayInputStream(encoding);
192        EncryptedDataStream encryptedData = new EncryptedDataStream(is);
193    
194        System.out.println("Information about the encrypted data:");
195        EncryptedContentInfoStream eci = encryptedData.getEncryptedContentInfo();
196        System.out.println("Content type: "+eci.getContentType().getName());
197        System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
198    
199        byte[] content = null;
200        // decrypt the message
201        try {
202          encryptedData.setupCipher(password);
203          InputStream decrypted = encryptedData.getInputStream();
204          ByteArrayOutputStream os = new ByteArrayOutputStream();
205          Util.copyStream(decrypted, os, null);
206          content = os.toByteArray();
207    
208          // get any unprotected attributes:
209          Attribute[] attributes = encryptedData.getUnprotectedAttributes();
210          if ((attributes != null) && (attributes.length > 0)) {
211            System.out.println("Attributes included: ");
212            // we know we have used content type
213            CMSContentType contentType = (CMSContentType)attributes[0].getAttributeValue();
214            System.out.println(contentType);  
215          }  
216    
217        } catch (InvalidKeyException ex) {
218          throw new CMSException("Key error: " + ex.toString());
219        } catch (NoSuchAlgorithmException ex) {
220          throw new CMSException("Content encryption algorithm not implemented: " + ex.toString());
221        } catch (InvalidAlgorithmParameterException ex) {
222          throw new CMSException("Invalid Parameters: " + ex.toString());
223        } catch (InvalidParameterSpecException ex) {
224          throw new CMSException("Content encryption algorithm not implemented: " + ex.toString());
225        } catch (CodingException ex) {
226          throw new CMSException("Error decoding attributes: " + ex.toString());
227        }
228        
229        return content;
230      }
231    
232      /**
233       * Starts the demo.
234       */
235      public void start() {
236         // the test message
237        String m = "This is the test message.";
238        System.out.println("Test message: \""+m+"\"");
239        System.out.println();
240        byte[] message = m.getBytes();
241        
242        // password (in real world use some more secure password)
243        char[] password = { 't', 'o', 'p', '-', 's', 'e', 'c', 'r', 'e', 't', '!' };
244    
245        try {
246          byte[] encoding;
247          byte[] received_message = null;
248          System.out.println("EnvelopedOutputStream implementation demo");
249          System.out.println("===========================");
250    
251    
252          //
253          // test CMS EncryptedDataOutputStream
254          //
255          System.out.println("\nEncryptedDataStream demo [create]:\n");
256          encoding = createEncryptedData(message, password);
257          // transmit data
258          System.out.println("\nEncryptedDataStream demo [parse]:\n");
259          received_message = getEncryptedData(encoding, password);
260          System.out.print("\nDecrypted content: ");
261          System.out.println(new String(received_message));
262    
263          if (CryptoUtils.equalsBlock(received_message, message) == false) {
264            throw new Exception("Decrypted content not equal to original one!");
265          }
266          System.out.println("Ready!");
267    
268        } catch (Exception ex) {
269          ex.printStackTrace();
270          throw new RuntimeException(ex.toString());
271        } finally {
272          if (password != null) {
273            for (int i = 0; i < password.length; i++) {
274              password[i] = '\u0000';
275            }
276          }
277        } 
278      }
279    
280    
281      /**
282       * Main method.
283       *
284       * @throws Exception
285       *            if an some error occurs 
286       */
287      public static void main(String argv[]) throws Exception {
288    
289        demo.DemoUtil.initDemos();
290    
291        (new EncryptedDataOutputStreamDemo()).start();
292        System.out.println("\nReady!");
293        DemoUtil.waitKey();
294      }
295    }