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/envelopedData/EnvelopedDataOutputStreamDemo.java 20 12.02.25 17:58 Dbratko $ 029// $Revision: 20 $ 030// 031 032 033package demo.cms.envelopedData; 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.EnvelopedDataOutputStream; 043import iaik.cms.EnvelopedDataStream; 044import iaik.cms.KeyTransRecipientInfo; 045import iaik.cms.RecipientInfo; 046import iaik.cms.attributes.CMSContentType; 047import iaik.security.random.SecRandom; 048import iaik.utils.CryptoUtils; 049import iaik.utils.Util; 050import iaik.x509.X509Certificate; 051 052import java.io.ByteArrayInputStream; 053import java.io.ByteArrayOutputStream; 054import java.io.IOException; 055import java.io.InputStream; 056import java.security.InvalidKeyException; 057import java.security.NoSuchAlgorithmException; 058import java.security.PrivateKey; 059import java.security.SecureRandom; 060 061import demo.DemoUtil; 062import demo.keystore.CMSKeyStore; 063 064 065/** 066 * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataOutputStream} and 067 * for encrypting data using the CMS type EnvelopedData. 068 */ 069public class EnvelopedDataOutputStreamDemo { 070 071 072 // encryption certificate of user 1 073 X509Certificate user1_crypt; 074 // encryption private key of user 1 075 PrivateKey user1_crypt_pk; 076 // encryption certificate of user 2 077 X509Certificate user2_crypt; 078 // encryption private key of user 2 079 PrivateKey user2_crypt_pk; 080 081 // secure random number generator 082 SecureRandom random; 083 084 /** 085 * Setup the demo certificate chains. 086 * 087 * Keys and certificate are retrieved from the demo KeyStore. 088 * 089 * @throws IOException if an file read error occurs 090 */ 091 public EnvelopedDataOutputStreamDemo() throws IOException { 092 093 System.out.println(); 094 System.out.println("**********************************************************************************"); 095 System.out.println("* EnvelopedDataOutputStream demo *"); 096 System.out.println("* (shows the usage of the CMS EnvelopedDataOutputStream implementation) *"); 097 System.out.println("**********************************************************************************"); 098 System.out.println(); 099 100 // encryption certs 101 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 102 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 103 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 104 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 105 106 random = SecRandom.getDefault(); 107 108 } 109 110 111 /** 112 * Creates a CMS <code>EnvelopedData</code> and wraps it into a ContentInfo. 113 * 114 * @param message the message to be enveloped, as byte representation 115 * @return the encoded EnvelopedData object just created, wrapped into a ContentInfo 116 * 117 * @throws CMSException if the <code>EnvelopedData</code> object cannot 118 * be created 119 * @throws IOException if an I/O error occurs 120 */ 121 public byte[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException { 122 123 124 // a stream from which to read the data to be encrypted 125 ByteArrayInputStream is = new ByteArrayInputStream(message); 126 127 // the stream to which to write the EnvelopedData 128 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 129 EnvelopedDataOutputStream envelopedData; 130 131 // wrap EnvelopedData into a ContentInfo 132 ContentInfoOutputStream contentInfoStream = 133 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream); 134 // create a new EnvelopedData object encrypted with AES 135 try { 136 envelopedData = new EnvelopedDataOutputStream(contentInfoStream, 137 (AlgorithmID)AlgorithmID.aes128_CBC.clone()); 138 } catch (NoSuchAlgorithmException ex) { 139 throw new CMSException("No implementation for AES."); 140 } 141 142 // create the recipient infos 143 RecipientInfo[] recipients = new RecipientInfo[2]; 144 // user1 is the first receiver 145 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 146 // user2 is the second receiver 147 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 148 149 // specify the recipients of the encrypted message 150 envelopedData.setRecipientInfos(recipients); 151 152 Attribute[] attributes = new Attribute[1]; 153 154 try { 155 // just for testing: set some unprotected attribute 156 // content type is data 157 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 158 attributes[0] = new Attribute(contentType); 159 envelopedData.setUnprotectedAttributes(attributes); 160 } catch (Exception ex) { 161 throw new CMSException("Error creating attribute: " + ex.toString()); 162 } 163 164 int blockSize = 16; // in real world we would use a block size like 2048 165 // write in the data to be encrypted 166 byte[] buffer = new byte[blockSize]; 167 int bytesRead; 168 while ((bytesRead = is.read(buffer)) != -1) { 169 envelopedData.write(buffer, 0, bytesRead); 170 } 171 172 // closing the stream finishes encryption and closes the underlying stream 173 envelopedData.close(); 174 return resultStream.toByteArray(); 175 } 176 177 /** 178 * Decrypts the encrypted content of the given EnvelopedData object for the 179 * specified recipient and returns the decrypted (= original) message. 180 * 181 * @param encoding the encoded EnvelopedData object, wrapped in a ContentInfo 182 * @param privateKey the private key to decrypt the message 183 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 184 * to which the specified private key belongs 185 * 186 * @return the recovered message, as byte array 187 * @throws CMSException if the message cannot be recovered 188 * @throws IOException if an I/O error occurs 189 */ 190 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws CMSException, IOException { 191 192 // create the EnvelopedData object from a DER encoded byte array 193 // we are testing the stream interface 194 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 195 196 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 197 198 System.out.println("Information about the encrypted data:"); 199 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 200 System.out.println("Content type: "+eci.getContentType().getName()); 201 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 202 203 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 204 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 205 for (int i=0; i<recipients.length; i++) { 206 System.out.println("Recipient "+(i+1)+":"); 207 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 208 } 209 210 // decrypt the message 211 try { 212 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 213 InputStream decrypted = enveloped_data.getInputStream(); 214 ByteArrayOutputStream os = new ByteArrayOutputStream(); 215 Util.copyStream(decrypted, os, null); 216 217 // get any unprotected attributes: 218 Attribute[] attributes = enveloped_data.getUnprotectedAttributes(); 219 if ((attributes != null) && (attributes.length > 0)) { 220 System.out.println("Attributes included: "); 221 // we know we have used content type 222 CMSContentType contentType = (CMSContentType)attributes[0].getAttributeValue(); 223 System.out.println(contentType); 224 } 225 226 return os.toByteArray(); 227 228 } catch (InvalidKeyException ex) { 229 throw new CMSException("Private key error: "+ex.toString()); 230 } catch (NoSuchAlgorithmException ex) { 231 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 232 } catch (CodingException ex) { 233 throw new CMSException("Cannot get unprotected attributes: "+ex.toString()); 234 } 235 236 } 237 238 /** 239 * Starts the demo. 240 */ 241 public void start() { 242 // the test message 243 String m = "This is the test message."; 244 System.out.println("Test message: \""+m+"\""); 245 System.out.println(); 246 byte[] message = m.getBytes(); 247 248 try { 249 byte[] encoding; 250 byte[] received_message = null; 251 System.out.println("EnvelopedDataOutputStream implementation demo"); 252 System.out.println("============================================="); 253 254 255 // 256 // test CMS EnvelopedDataStream 257 // 258 System.out.println("\nEnvelopedData demo [create]:\n"); 259 encoding = createEnvelopedDataStream(message); 260 // transmit data 261 System.out.println("\nEnvelopedData demo [parse]:\n"); 262 // user1 means index 0 (hardcoded for this demo) 263 received_message = getEnvelopedDataStream(encoding, user1_crypt_pk, 0); 264 System.out.print("\nDecrypted content: "); 265 System.out.println(new String(received_message)); 266 267 if (CryptoUtils.equalsBlock(received_message, message) == false) { 268 throw new Exception("Decrypted content not equal to original one!"); 269 } 270 271 System.out.println("Ready!"); 272 273 } catch (Exception ex) { 274 ex.printStackTrace(); 275 throw new RuntimeException(ex.toString()); 276 } 277 } 278 279 280 /** 281 * Main method. 282 * 283 * @throws Exception 284 * if an some error occurs 285 */ 286 public static void main(String argv[]) throws Exception { 287 288 demo.DemoUtil.initDemos(); 289 290 (new EnvelopedDataOutputStreamDemo()).start(); 291 292 DemoUtil.waitKey(); 293 } 294}