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