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}