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